zoukankan      html  css  js  c++  java
  • 文本离散表示(二):新闻语料的one-hot编码

    上一篇博客介绍了文本离散表示的one-hotTF-IDFn-gram方法,在这篇文章里,我做了一个对新闻文本进行one-hot编码的小实践。

    文本的one-hot相对而言比较简单,我用了两种方法,一种是自己造轮子,第二种是用深度学习框架keras来做。同时,我发现尽管sklearn可以实现对特征向量的one-hot,但并不适用于文本的处理。

    代码和新闻文本文件可到我github主页下载:https://github.com/DengYangyong/one_hot_distribution。发现新闻文本文件好大。。。不该上传完整的。

    一、关于sklearn进行one-hot

    在翻阅介绍文本one-hot的博客时,看到有博主举了一个用sklearn实现one-hot的小例子,我也把代码运行了一遍。

    代码和输出结果如下:

    from sklearn import preprocessing  
    import numpy as np
    
    enc = preprocessing.OneHotEncoder()  # 创建对象
    array1 = np.asarray([[0,0,3],[1,1,0],[0,2,1],[1,0,2]])
    enc.fit(array1)   # 拟合
    array2 = enc.transform([[0,1,3]]).toarray()  # 转化
    print("4个样本的特征向量是:
    ", array1,'
    ')
    print('对第5个样本的特征向量进行one-hot:', array2)

     输出结果:

    4个样本的特征向量是:
     [[0 0 3]
     [1 1 0]
     [0 2 1]
     [1 0 2]] 
    
    对第5个样本的特征向量进行one-hot: [[1. 0. 0. 1. 0. 0. 0. 0. 1.]]

    什么意思呢?拿西瓜做为例子,有4个西瓜,每个西瓜取3个特征,{"瓜籽": ['有', '无'], "根蒂": ['硬挺','蜷缩','枯萎'], "颜色": ['白','黑','绿','黄']},第一个特征有两个属性,用0和1表示,第二个特征有三个属性,用0、1和2表示,第三个特征有4个属性,用0-3表示。所以array1这个4*3的数组表示4个西瓜的特征向量,4表示样本数,3表示特征数。那么在进行one-hot时,每个样本的向量的维度是9(2+3+4)维,也就是属性的数量进行累加。样本第一个特征取0时,表示为[1,0]; 第二个特征为1时,表示为[0,1,0];第三个特征为3时,表示为[0,0,0,1],然后按顺序拼成一个向量,就得到了[1,0,0,1,0,0,0,0,1],这个9维的向量也就是第5个西瓜的特征向量的one-hot。

    那可以用来做文本的one-hot吗?不可以,因为上面的例子是把向量转为了one-hot,而文本one-hot是把类似[['天', '气', '很','好'],['我','爱','自','然','语','言','处','理']]这样的形式转为one-hot,这样的形式转为向量要么是词频矩阵,要么直接就是one-hot。所以sklearn的这个函数是没法做文本的one-hot的。

    二、自己造轮子实现文本one-hot

    这次是对新闻文本进行one-hot,cnews.train.txt 这个文件中有几十万篇新闻文本,有10类新闻,分别是 ['体育', '财经', '房产', '家居', '教育', '科技', '时尚', '时政', '游戏', '娱乐']。文件的每一行就是一篇新闻,开头两个字是类别,后面是新闻内容,中间用" "隔开。比如第一行的内容:

    体育    马晓旭意外受伤让国奥警惕 无奈大雨格外青睐殷家军记者傅亚雨沈阳报道 来到沈阳,国奥队依然没有摆脱雨水的困扰。......

    考虑到内容较大,因此只读取1000篇新闻文本进行one-hot。经过了几个步骤:把文本处理成char-level的列表——取前1000个词频最高的字作为字典——为字典的每个字分配索引——进行one-hot编码。

    最终得到的是1000*1000维的one-hot数组。打开保存的文本一看,密密麻麻的0,星星点点的1,足以看到数据的稀疏性。

    import numpy as np
    import os
    
    text_path = os.getcwd()+os.sep+'cnews'+os.sep
    fp = open(text_path+'cnews.train.txt','r',encoding='utf8')
    fout = open(text_path+'cnews_one_hot_diy.txt','w',encoding='utf8')
    
    #把新闻处理成char_level的列表,去除停用词
    # 输出:[['马', '晓', '旭', '意', '外', '受', '伤', '国', '奥', '警', '惕', '', '奈',...],[],...[]]
    news_list = []
    news_count =0
    fp_stop = open("ChineseStopWords.txt","r",encoding="utf8") #打开中文停用词表用于过滤
    dict_stopwords = [stopwords.strip() for stopwords in fp_stop.readlines()]
    for text in fp.readlines() :
        if text.strip() and news_count < 1000: #只取前1000篇新闻文本
            label,news = text.strip().split('	')  #把类别标签和文本分开
            news_list.append([char.strip() for char in list(news) if char not in dict_stopwords]) # 把文本切成一个个字并过滤停用词
            news_count += 1
    print("读取了cnews.train.txt 中的"+str(news_count)+"条新闻。
    ")
    fp_stop.close()
    fp.close()
    
    #把每个字的词频统计出来
    # 输出:{'马': 1379, '晓': 31, '旭': 25, '意': 855, '外': 1218, '受': 872, '伤': 1169, '国': 1046, ...}
    token_count = {}
    for list_ in news_list:
        for word in list_:
            if word not in token_count:
                token_count[word] = 1
            else:
                token_count[word] += 1
                
    # 按词频对字进行排序,取前1000个词频最高的字
    # token_sort输出:{'分': 8759, '球': 8193, '场': 7862, '斯': 7731, '赛': 7040, '中': 6537, '队': 6169, ...}
    # dict.items()是把字典转化为元素为元祖的列表,然后按照元祖的第2个值进行排序,即词频。
    token_tuple = sorted(token_count.items(), key=lambda item: item[1],reverse=True) 
     # 取前1000个词频最高的字,转化为字典。
    token_sort = dict(token_tuple[:1000])
    
    #为每一个字分配索引,不为0分配字,貌似是深度学习中的习惯。
    # 输出:{'分': 1, '球': 2, '场': 3, '斯': 4, '赛': 5, '中': 6, '队': 7, '时': 8, '出': 9, '篮': 10, '火': 11, '次': 12, '前': 13, ...}
    token_index = dict(zip(token_sort.keys(),range(1,len(token_sort))))
    
    # 进行进行one-hot编码
    # result[0]输出为:array([0., 1., 1., 1., 0., 1., 1., 1., 1., 1., 0., 0., 1., 1., 0., 0., 0.,..])
    result = np.zeros(shape=(1000,1000))
    for i,list_ in enumerate(news_list):
        for word in list_:
            if word in token_index:
                j = token_index[word]
                result[i,j] = 1
                
    # 用numpy 将one-hot按int格式保存,方便下次打开为ndarray格式,而不是文本。
    np.savetxt(text_path+"cnews_one_hot_diy.txt",result,fmt="%d")
    fout.close()

    第一篇文本的one-hot:

    三、 用keras实现one-hot

    使用keras需要先安装tensorflow,用宇宙最牛公司的深度学习框架来做one-hot,有杀鸡用牛刀的感觉,所以说这是豪华版。不过可以通过这个过程来掌握一些python库的用法,也是非常有意思的。过程比较简单,就两步:文本处理成char-level的列表——进行one-hot编码。

    不多说了,上代码!

    #coding=utf8
    from keras.preprocessing.text import Tokenizer
    from collections import Counter
    import os
    from itertools import chain
    import numpy as np
    
    text_path = os.getcwd()+os.sep+'cnews'+os.sep
    fp = open(text_path+'cnews.train.txt','r',encoding='utf8')
    fout = open(text_path+'cnews_one_hot.txt','w',encoding='utf8')
    news_list = []
    
    #把新闻处理成char_level的列表,去除停用词
    # 输出:[['马', '晓', '旭', '意', '外', '受', '伤', '国', '奥', '警', '惕', '', '奈',...],[],...[]]
    news_count =0
    fp_stop = open("ChineseStopWords.txt","r",encoding="utf8")
    dict_stopwords = [stopwords.strip() for stopwords in fp_stop.readlines()]
    for text in fp.readlines() :
        if text.strip() and news_count < 1000:
            label,news = text.strip().split('	')
            news_list.append([char.strip() for char in list(news) if char not in dict_stopwords])
            news_count += 1
    print("读取了cnews.train.txt 中的"+str(news_count)+"条新闻。
    ")
    fp_stop.close()
    fp.close()
    
    # 统计并查看这些新闻中所有的字的个数
    # 其实对于这个问题而言不重要
    counter = Counter(list(chain.from_iterable(news_list)))
    char_count = len(counter)
    print(str(news_count)+"条新闻中有"+str(char_count)+"个不同的字。
    ")
    
    #用keras进行one-hot编码,取前1000个频率最高的字作为字典
    tokenizer=Tokenizer(num_words=1000)
    tokenizer.fit_on_texts(news_list) 
    one_hot_results = tokenizer.texts_to_matrix(news_list,mode='binary') #直接由[['马', '晓', '旭', '意', ...],..[]]得到one-hot矩阵,还是方便。
    print("独热编码后每条新闻的向量长度为:", one_hot_results.shape[1],'
    ')
    
    # 用numpy把array数组保存,方便下次再以arry的格式打开,而不是字符串格式。
    np.savetxt(text_path+"cnews_one_hot.txt",one_hot_results,fmt="%d")
    fout.close()
    
    # 将保存的array数组文件打开
    news_one_hot = np.loadtxt(text_path+"cnews_one_hot.txt",dtype=float)
    print("数据的格式为", type(news_one_hot))

     打印的内容:

    读取了cnews.train.txt 中的1000条新闻。
    
    1000条新闻中有3164个不同的字。
    
    独热编码后每条新闻的向量长度为: 1000 
    
    数据的格式为 <class 'numpy.ndarray'>

     第一篇文本的one-hot:

    参考资料:

    《Python深度学习》

  • 相关阅读:
    异常:java.io.IOException: Too many open files:
    转载 Servlet3.0中使用注解配置Servle
    Spring 源码从github导入源码到idea2016
    git 命令
    常用linux命令
    mysql优化常用语句
    mysql中in、not in、exists和not exists的区别
    mysql优化
    php常用的数据结构算法
    算法(一)
  • 原文地址:https://www.cnblogs.com/Luv-GEM/p/10544393.html
Copyright © 2011-2022 走看看