文本表示(一)

文本表示的意思是把字词处理成向量或矩阵,以便计算机能进行处理。

文本表示按照细粒度可划分:字级别、词语级别、句子级别的文本表示。

文本表示可以分为两大类:

  • 离散表示(特征:离散、高维、稀疏):one-hot、BOW、TF-IDF、n-gram
  • 分布式表示(特征:连续、低维、稠密):word2vec、Glove、ELMO、GPT、BERT。

文本使用one-hot 编码步骤:

  1. 根据语料库创建 词典(vocabulary),并创建词和索引的 映射(stoi,itos);
  2. 将句子转换为用索引表示;
  3. 创建OneHot 编码器;
  4. 使用OneHot 编码器对句子进行编码;

One-Hot 编码的特点如下:

  1. 词向量长度是词典长度;
  2. 在向量中,该单词的索引位置的值为 1 ,其余的值都是 0 ;
  3. 使用One-Hot 进行编码的文本,得到的矩阵是稀疏矩阵(sparse matrix)

缺点:

  1. 不同词的向量表示互相正交,无法衡量不同词之间的关系;
  2. 该编码只能反映某个词是否在句中出现,无法衡量不同词的重要程度;
  3. 使用One-Hot 对文本进行编码后得到的是高维稀疏矩阵,会浪费计算和存储资源;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import jieba
from sklearn.preprocessing import LabelBinarizer 
texts = ['我是陨石坑', '书山有路苦作舟', '学海无涯勤为径']

# 切词去重
words_list = [" ".join(jieba.cut(text, cut_all=False)).split(' ') for text in texts]
words = list(set([word for words in words_list for word in words]))
# word_dict = {word: index for index, word in enumerate(words)}


# 初始化编码器
lb = LabelBinarizer()
lb.fit_transform(words)
print(lb.classes_)
# 单句编码
one_hot = lb.transform(words_list[0])
print(one_hot)
# 解码
print(lb.inverse_transform(one_hot))

BOW(Bag of Words):在词袋模型中不考虑语序和词法的信息(降低了one-hot带来的维度灾难),每个单词都是相互独立的,将词语放入一个“袋子”里,统计每个单词出现的频率。

词袋模型编码特点:

  1. 词袋模型是对文本(而不是字或词)进行编码;
  2. 编码后的向量长度是词典的长度;
  3. 该编码忽略词出现的次序;
  4. 在向量中,该单词的索引位置的值为单词在文本中出现的次数;如果索引位置的单词没有在文本中出现,则该值为 0 ;

缺点

  1. 该编码忽略词的位置信息,位置信息在文本中是一个很重要信息,词的位置不一样语义会有很大的差别(如 “我爱你” 和 “你爱我” 的编码一样);
  2. 该编码方式虽然统计了词在文本中出现的次数,但仅仅通过“出现次数”这个属性无法区分常用词(如:“我”、“是”、“的”等)和关键词(如:“陨石坑”,“勤为径”等)在文本中的重要程度;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import jieba
from sklearn.feature_extraction.text import CountVectorizer 
texts = ['我是陨石坑,也是钥匙扣', '我是应收款,也是预付款', '书山有路苦作舟', '学海无涯勤为径']
corpus = [" ".join(jieba.cut(text, cut_all=False)) for text in texts]

# vectorizer=CountVectorizer()                                            #自动过滤英文单字符
# vectorizer=CountVectorizer(token_pattern='\w{1,}')                      #不过滤英文单字符
vectorizer=CountVectorizer(token_pattern='[\u4e00-\u9fa5_a-zA-Z0-9]{1,}') #正则表达式匹配中文或数字或字母出现一次会更多次
print("词频统计:")
#输出4个文本的词频统计:左边的括号中的两个数字分别为(文本序号,词序号),右边数字为频次
print(vectorizer.fit_transform(corpus))
print("\n词袋模型:\n", vectorizer.fit_transform(corpus).toarray())
print("\n词库:", vectorizer.get_feature_names())

# 降维
from sklearn.feature_extraction.text import HashingVectorizer 
vectorizerH=HashingVectorizer(n_features = 4,norm = None, token_pattern='[\u4e00-\u9fa5_a-zA-Z0-9]{1,}') #将12维词汇表哈希降维到4维
print("词频统计:")
print(vectorizerH.fit_transform(corpus))
print("\n词袋模型:")
print(vectorizerH.fit_transform(corpus).toarray())

TF-IDF是一种用于信息检索与数据挖掘的常用加权技术。TF意思是词频(Term Frequency),IDF意思是逆文本频率指数(Inverse Document Frequency)

字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。一个词语在一篇文章中出现次数越多, 同时在所有文档中出现次数越少, 越能够代表该文章。

$TF-IDF(t,d) = TF(t,d)*IDF(t)$

$TF(t,d)$ 表示 词语 $t$ 在文档 $d$ 中出现的频率, $IDF(t)$ 是逆文本频率指数,它可以衡量单词 $t$ 用于区分这篇文档和其他文档的重要性。$IDF$ 的公式如下,分母加1是为了避免分母为0

$IDF = log(\displaystyle \frac{语料库的文档总数}{包含该词的文档数+1})$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import jieba
from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.feature_extraction.text import TfidfTransformer  
texts = ['我是陨石坑,也是钥匙扣', '我是应收款,也是预付款', '书山有路苦作舟', '学海无涯勤为径']
corpus = [" ".join(jieba.cut(text, cut_all=False)) for text in texts]

# vectorizer=CountVectorizer()                                            #自动过滤英文单字符
# vectorizer=CountVectorizer(token_pattern='\w{1,}')                      #不过滤英文单字符
vectorizer=CountVectorizer(token_pattern='[\u4e00-\u9fa5_a-zA-Z0-9]{1,}') #正则表达式匹配中文或数字或字母出现一次会更多次
cell = vectorizer.fit_transform(corpus)

#类调用  
transformer = TfidfTransformer()   
#将词频矩阵X统计成TF-IDF值  
tfidf = transformer.fit_transform(cell)  
#查看数据结构 tfidf[i][j]表示i类文本中的tf-idf权重  
print(tfidf.toarray())

n-gram 是一种语言模型(Language Model, LM)。语言模型是一种基于概率的判别式模型,该模型的输入是一句话(单词的序列),输出的是这句话的概率,也就是这些单词的联合概率(joint probability)。(备注:语言模型就是判断一句话是不是正常人说的。)

共现矩阵(Co-Occurrence Matrix)是首先指定窗口大小,然后统计窗口(和对称窗口)内词语共同出现的次数作为词的向量(vector)。

语料库:

1、我 是 陨石坑

2、我 是 应收款

3、书山 有路 苦作舟

4、学海无涯 勤为径

采用指定窗口大小为1(即:左右的 window_length=1,相当于 bi-gram==2-gram) 统计数据如下: (我,是),(是,陨石坑),(我,是),(是,应收款),(书山,有路),(有路,苦作舟),(学海无涯,勤为径)

共现矩阵如下:

counts 陨石坑 钥匙扣 书山 有路 苦作舟 学海无涯 勤为径
0 2 0 0 0 0 0 0 0
2 0 1 1 0 0 0 0 0
陨石坑 0 1 0 0 0 0 0 0 0
钥匙扣 0 1 0 0 0 0 0 0 0
书山 0 0 0 0 0 1 0 0 0
有路 0 0 0 0 1 0 1 0 0
苦作舟 0 0 0 0 0 1 0 0 0
学海无涯 0 0 0 0 0 0 0 0 1
勤为径 0 0 0 0 0 0 0 1 0

优点

  1. 相较于词袋模型考虑了句子中词的顺序;

缺点

  1. 词表的长度很大,导致词的向量长度也很大;
  2. 共现矩阵也是稀疏矩阵(可以使用 SVDPCA 等算法进行降维,但是计算量很大);
  3. 无法建模更远的关系,语料的不足使得无法训练更高阶的语言模型。
  4. 无法建模出词之间的相似度。
  5. 训练语料里面有些 n 元组没有出现过,其对应的条件概率就是 0,导致计算一整句话的概率为 0。解决这个问题有两种常用方法: 平滑法和回退法。