中文的分词和文档的数字表示
要让电脑或是任何NLP 模型理解一篇新闻标题在说什么,我们不能将自己已经非常习惯的语言文字直接扔给电脑,而是要转换成它熟悉的形式:数字。
这里我们将一篇新闻标题视为一个“文档”,在中文的语言特征里,文档的基本单位主要由词构成。不同于英文的是,中文句子中没有词的界限,因此进行中文文档的数字表示时,通常需要先做分词以及词的编码。
常见的基于中文分词算法有:正向最大匹配法、逆向最大匹配法、双向匹配法、最优匹配法、联想-回溯法等。
借助 Jieba 这个中文分词工具,可以轻松实现中文的分词。
import jieba.posseg as pseg
text = '一片大蒜轻松鉴别地沟油'
words = pseg . cut ( text )
[ word for word in words ]
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\HP\AppData\Local\Temp\jieba.cache
Loading model cost 0.677 seconds.
Prefix dict has been built successfully.
[pair('一片', 'm'),
pair('大蒜', 'n'),
pair('轻松', 'a'),
pair('鉴别', 'v'),
pair('地沟油', 'n')]
TRAIN_CSV_PATH = "./train.csv"
import pandas as pd
train = pd . read_csv ( TRAIN_CSV_PATH , index_col = 0 )
cols = [ 'title1_zh' , 'title2_zh' , 'label' ]
train = train . loc [:, cols ]
train_nan = train[train[['title1_zh','title2_zh']].isnull().any(axis=1)] #训练集空值检查
train_nan['title2_zh'].apply(lambda x: pd.isna(x))
id
48473 True
64312 True
220922 True
243368 True
247695 True
259536 True
291779 True
Name: title2_zh, dtype: bool
我们可以利用 Pandas 的 apply 函数,将一个自定义的函数 jieba_tokenizer 套用到所有新闻标题A 以及B 之上,做文本分词:
def jieba_tokenizer ( text ):
if not(pd.isna(text)):
words = pseg . cut ( text )
return ' ' . join ([ word for word , flag in words if flag != 'x' ]) ##标点符号去除
return ' '
train [ 'title1_tokenized' ] = \
train . loc [:, 'title1_zh' ] \
. apply ( jieba_tokenizer )
train [ 'title2_tokenized' ] = \
train . loc [:, 'title2_zh' ] \
. apply ( jieba_tokenizer )
分词过程出乎意料的漫长,我们用 pickle 把结果持久化保存下来,以便之后可以继续快速使用。
import pickle
pkl_file = open('./save_file', 'wb')
pickle.dump(train,pkl_file,pickle.HIGHEST_PROTOCOL)
pkl_file.close()
pkl_file_rb = open(r'./save_file', 'rb')
new_data =pickle.load(pkl_file_rb)
new_data.head(2)
title1_zh | title2_zh | label | title1_tokenized | title2_tokenized | |
---|---|---|---|---|---|
id | |||||
0 | 2017养老保险又新增两项,农村老人人人可申领,你领到了吗 | 警方辟谣“鸟巢大会每人领5万” 仍有老人坚持进京 | unrelated | 2017 养老保险 又 新增 两项 农村 老人 人人 可 申领 你 领到 了 吗 | 警方 辟谣 鸟巢 大会 每人 领 5 万 仍 有 老人 坚持 进京 |
3 | "你不来深圳,早晚你儿子也要来",不出10年深圳人均GDP将超香港 | 深圳GDP首超香港?深圳统计局辟谣:只是差距在缩小 | unrelated | 你 不 来 深圳 早晚 你 儿子 也 要 来 不出 10 年 深圳 人均 GDP 将 超 香港 | 深圳 GDP 首 超 香港 深圳 统计局 辟谣 只是 差距 在 缩小 |
下面一段内容我们先从李孟博士的博客里绕开,尝试一些更为简洁的NLP方法。
完成分词之后,就可以进行文档的数字化表示。
可用的方法有很多,我们首先从常用的 TF-IDF文档向量表示方法入手。TF-IDF是一种向量空间模型(VSM:Vector Space Model),VSM中每个文档d可以用词典标引项向量来表示V(d)=(W1,W2,…Wm),Wi对应第i个标引词的权重。
VSM中权重的计算有:布尔权重(下图中的示例,仅标识文档中是否包含某词)、词频权重、TF-IDF权重、熵权重等,其中最广泛采用的便是TF-IDF权重。
TF-IDF的主要思想是,如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
TF词频(Term Frequency)指的是某一个给定的词语在该文件中出现的次数。
IDF逆文档频率(Inverse Document Frequency)的主要思想是:如果包含词条的文档越少,IDF越大,则说明词条具有很好的类别区分能力。
将TF和IDF相乘,形成TF-IDF度量:TF-IDF(d,t)= TF(d,t) * IDF(t) 。
我们可以使用 sklearn 包中的 TfidfVectorizer 函数实现文档的TF-IDF向量化表示。
from sklearn.feature_extraction.text import TfidfVectorizer
corpus = pd.concat([train . title1_tokenized, train . title2_tokenized])
corpus = [c for c in corpus]
corpus
['2017 养老保险 又 新增 两项 农村 老人 人人 可 申领 你 领到 了 吗',
'你 不 来 深圳 早晚 你 儿子 也 要 来 不出 10 年 深圳 人均 GDP 将 超 香港',
'你 不 来 深圳 早晚 你 儿子 也 要 来 不出 10 年 深圳 人均 GDP 将 超 香港',
'你 不 来 深圳 早晚 你 儿子 也 要 来 不出 10 年 深圳 人均 GDP 将 超 香港',
'用 大蒜 鉴别 地沟油 的 方法 怎么 鉴别 地沟油',
'你 不 来 深圳 早晚 你 儿子 也 要 来 不出 10 年 深圳 人均 GDP 将 超 香港',
'吃 榴莲 的 禁忌 吃 错会 致命',
...]
vectorizer = TfidfVectorizer(min_df=1)
vectorizer.fit_transform(corpus)
<641104x67243 sparse matrix of type ''
with 5064277 stored elements in Compressed Sparse Row format>
我们看到,通过训练得到了一个包含67243 个标引词的词典,每个新闻标题被转换为一个67243维的TF-IDF 向量表示。
好了,就到这儿。
本系列共12期,将分6天发布。相关代码已全部分享在我的github项目(moronism189/chinese-nlp-stepbystep)下,急的可以先去那里下载。