null 文本数据处理(自然语言处理基础) - K码农

文本数据的特征提取,中文分词及词袋模型

1.使用CountVectorizer对文本进行特征提取

1
2
3
4
5
6
7
8
9
#导入量化工具CountVectorizer工具
from sklearn.feature_extraction.text import CountVectorizer
vect = CountVectorizer()
#使用CountVectorizer拟合文本数据
en = ['The quick brown fox jumps over a lazy dog']
vect.fit(en)
#打印结果
print('单词数:{}'.format(len(vect.vocabulary_)))
print('分词:{}'.format(vect.vocabulary_))
单词数:8
分词:{'the': 7, 'quick': 6, 'brown': 0, 'fox': 2, 'jumps': 3, 'over': 5, 'lazy': 4, 'dog': 1}
1
2
3
4
5
6
7
#使用中文文本进行试验
cn = ['那只敏捷的综色狐狸跳过了一只懒惰的狗']
#拟合中文文本数据
vect.fit(cn)
#打印结果
print('单词数:{}'.format(len(vect.vocabulary_)))
print('分词:{}'.format(vect.vocabulary_))
单词数:1
分词:{'那只敏捷的综色狐狸跳过了一只懒惰的狗': 0}

2.使用分词工具对中文文本进行分词

1
2
3
4
5
6
7
8
#导入结巴分词
import jieba
#使用结巴分词对中文文本进行分词
cn = jieba.cut('那只敏捷的棕色狐狸跳过了一只懒惰的狗')
#使用空格作为词之间的分界线
cn = [' '.join(cn)]
#打印结果
print(cn)
['那 只 敏捷 的 棕色 狐狸 跳过 了 一只 懒惰 的 狗']
1
2
3
4
5
#使用CountVectorizer对中文文本进行向量化
vect.fit(cn)
#打印结果
print('单词数:{}'.format(len(vect.vocabulary_)))
print('分词:{}'.format(vect.vocabulary_))
单词数:6
分词:{'敏捷': 2, '棕色': 3, '狐狸': 4, '跳过': 5, '一只': 0, '懒惰': 1}

3.使用词袋模型将文本数据转为数组

1
2
3
4
#定义词袋模型
bag_of_words = vect.transform(cn)
#打印词袋模型中的数据特征
print('转化为词袋的特征:\n{}'.format(repr(bag_of_words)))
转化为词袋的特征:
<1x6 sparse matrix of type '<class 'numpy.int64'>'
	with 6 stored elements in Compressed Sparse Row format>
1
2
#打印词袋模型的密度表达
print('词袋的密度表达:\n{}'.format(bag_of_words.toarray()))
词袋的密度表达:
[[1 1 1 1 1 1]]
1
2
3
4
5
6
#输入新的中文文本
cn_1 = jieba.cut('懒惰的狐狸不如敏捷的狐狸敏捷,敏捷的狐狸不如懒惰的狐狸懒惰')
#以空格进行分隔
cn2 = [' '.join(cn_1)]
#打印结果
print(cn2)
['懒惰 的 狐狸 不如 敏捷 的 狐狸 敏捷 , 敏捷 的 狐狸 不如 懒惰 的 狐狸 懒惰']
1
2
3
4
5
#建立新的词袋模型
new_bag = vect.transform(cn2)
#打印结果
print('转化为词袋的特征:\n{}'.format(repr(new_bag)))
print('词袋的密度表达:\n{}'.format(new_bag.toarray()))
转化为词袋的特征:
<1x6 sparse matrix of type '<class 'numpy.int64'>'
	with 3 stored elements in Compressed Sparse Row format>
词袋的密度表达:
[[0 3 3 0 4 0]]

对文本数据进一步优化处理

1.使用n-Gram改善词袋模型

1
2
3
4
5
6
7
8
9
10
#使用n-Gram改善词袋模型
#那就写一句话吧
joke = jieba.cut('小明看见了小李骑了夏丽的脚踏车')
#插入空格
joke = [' '.join(joke)]
#转化为向量
vect.fit(joke)
joke_feature = vect.transform(joke)
#打印文本数据特征
print('这句话的特征表达:\n{}'.format(joke_feature.toarray()))
这句话的特征表达:
[[1 1 1 1 1]]
1
2
3
4
5
6
7
8
#将刚才的文本打乱顺序
joke2 = jieba.cut('小李看见夏丽骑了小明的脚踏车')
#插入空格
joke2 = [' '.join(joke2)]
#进行特征提取
joke2_feature = vect.transform(joke2)
#打印文本的特征
print('这句话的特征表达:\n{}'.format(joke2_feature.toarray()))
这句话的特征表达:
[[1 1 0 1 1]]
1
2
3
4
5
6
7
8
#修改CountVectorizer的ngram参数
vect = CountVectorizer(ngram_range=(2,2))
#重新进行文本数据的特征提取
cv = vect.fit(joke)
joke_feature = cv.transform(joke)
#打印新的结果
print('调整n-Gram参数后的词典:{}'.format(cv.get_feature_names()))
print('新的特征表达:{}'.format(joke_feature.toarray()))
调整n-Gram参数后的词典:['夏丽 脚踏车', '小明 看见', '李骑 夏丽', '看见 李骑']
新的特征表达:[[1 1 1 1]]
1
2
3
4
5
6
7
#调整文本顺序
joke2 = jieba.cut('小李看见夏丽骑了小明的脚踏车')
#插入空格
joke2 = [' '.join(joke2)]
#提取文本数据特征
joke2_feature = vect.transform(joke2)
print('新的特征表达:{}'.format(joke2_feature.toarray()))
新的特征表达:[[0 0 0 0]]
  • 在调整了CountVectorizer的ngram_range参数之后,机器不再认为这两句是同一个意思了,所以n-Gram模型对文本特征提取进行了很好的优化

2.使用tf-idf模型对文本数据进行处理

1
2
#显示ACLIMDB数据集的树状文件夹列表
!tree ACLIMDB
卷 Data 的文件夹 PATH 列表
卷序列号为 06B1-81F6
D:\JUPYTERNOTEBOOK\ACLIMDB
├─test
│  ├─neg
│  └─pos
└─train
    ├─neg
    ├─pos
    └─unsup
1
2
3
4
5
6
7
8
9
10
11
#导入量化工具CountVectorizer工具
from sklearn.feature_extraction.text import CountVectorizer
#导入文件载入工具
from sklearn.datasets import load_files
#定义训练数据集
train_set = load_files('aclImdb/train')
X_train,y_train, = train_set.data,train_set.target
#打印训练数据集文件数量
print('训练集文件数量:{}'.format(len(X_train)))
#随便抽取一条影评打印出来
print('随机抽一个看看:',X_train[22])
训练集文件数量:75000
随机抽一个看看: b"Okay, once you get past the fact that Mitchell and Petrillo are Dean and Jerry knockoffs, you could do worse than this film. Charlita as Princess Nona is great eye candy, Lugosi does his best with the material he's given, and the production values, music especially (except for the vocals) are better than you'd think for the $50k cost of production. The final glimpses of the characters are a hoot. Written by Tim Ryan, a minor actor in late Charlie Chan films, and husband of Grannie on the Beverly Hillbillies. All in all, WAY better than many late Lugosi cheapies."
1
2
3
4
5
#载入测试集
test = load_files('aclImdb/test/')
X_test,y_test = test.data,test.target
#返回测试数据集文件的数量
print(len(X_test))
25000
1
2
3
4
5
6
7
8
9
10
#用CountVectorizer拟合训练数据集
vect = CountVectorizer().fit(X_train)
#将文本转化为向量
X_train_vect = vect.transform(X_train)
#把测试数据集转化为向量
X_test_vect = vect.transform(X_test)
#打印训练集特征数量
print('训练集样本特征数量:{}'.format(len(vect.get_feature_names())))
#打印最后10个训练集样本特征
print('最后10个训练集样本特征:{}'.format(vect.get_feature_names()[-10:]))
训练集样本特征数量:124255
最后10个训练集样本特征:['üvegtigris', 'üwe', 'ÿou', 'ıslam', 'ōtomo', 'şey', 'дом', 'книги', '色戒', 'rock']
1
2
3
4
5
6
7
8
9
10
#导入tfidf转化工具
from sklearn.feature_extraction.text import TfidfTransformer
#用tfidf工具转化训练集和测试集
tfidf = TfidfTransformer(smooth_idf = False)
tfidf.fit(X_train_vect)
X_train_tfidf = tfidf.transform(X_train_vect)
X_test_tfidf = tfidf.transform(X_test_vect)
#将处理前后的特征打印进行比较
print('未经tfidf处理的特征:\n',X_train_vect[:5,:5].toarray())
print('经过tfidf处理的特征:\n',X_train_tfidf[:5,:5].toarray())
未经tfidf处理的特征:
 [[0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]
经过tfidf处理的特征:
 [[0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0.]]
1
2
3
4
5
6
7
8
9
10
11
12
13
#导入线性SVC分类模型
from sklearn.svm import LinearSVC
#导入交叉验证工具
from sklearn.model_selection import cross_val_score
#使用交叉验证对模型进行评分
scores = cross_val_score(LinearSVC(),X_train_vect,y_train,cv=3)
#重新训练线性SVC模型
clf = LinearSVC().fit(X_train_tfidf,y_train)
#使用新数据进行交叉验证
scores2 = cross_val_score(LinearSVC(),X_train_tfidf,y_train,cv=3)
#打印新的分数进行对比
print('经过tf-idf处理的训练集交叉验证得分:{:.3f}'.format(scores.mean()))
print('经过tf-id处理的测试集得分:{:.3f}'.format(clf.score(X_test_tfidf,y_test)))
经过tf-idf处理的训练集交叉验证得分:0.660
经过tf-id处理的测试集得分:0.144

3.删除文本中的停用词

1
2
3
4
5
6
#导入内置的停用词库
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
#打印停用词个数
print('停用词个数:',len(ENGLISH_STOP_WORDS))
#打印停用词中前20个和后20个
print('列出前20个和后20个:\n',list(ENGLISH_STOP_WORDS)[:20],list(ENGLISH_STOP_WORDS)[-20:])
停用词个数: 318
列出前20个和后20个:
 ['interest', 'meanwhile', 'do', 'thereupon', 'can', 'cry', 'upon', 'then', 'first', 'six', 'except', 'our', 'noone', 'being', 'done', 'afterwards', 'any', 'even', 'after', 'otherwise'] ['seemed', 'top', 'as', 'all', 'found', 'very', 'nor', 'seem', 'via', 'these', 'been', 'beforehand', 'behind', 'becomes', 'un', 'ten', 'onto', 'ourselves', 'an', 'keep']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#导入Tfidf模型
from sklearn.feature_extraction.text import TfidfVectorizer
#激活英文停用词参数
tfidf = TfidfVectorizer(smooth_idf = False,stop_words = 'english')
#拟合训练数据集
tfidf.fit(X_train)
#将训练数据集文本转化为向量
X_train_tfidf = tfidf.transform(X_train)
#使用交叉验证进行评分
scores3 = cross_val_score(LinearSVC(),X_train_tfidf,y_train,cv=3)
clf.fit(X_train_tfidf,y_train)
#将测试数据集转化为向量
X_test_tfidf = tfidf.transform(X_test)
#打印交叉验证评分和测试集评分
print('去掉停用词后训练集交叉验证平均分:{:3f}'.format(scores3.mean()))
print('去掉停用词后测试集模型得分:{:3f}'.format(clf.score(X_test_tfidf,y_test)))
去掉停用词后训练集交叉验证平均分:0.723933
去掉停用词后测试集模型得分:0.150920

 

总结 : 

  在scikit-learn中,有两个类使用了tf-idf方法,其中一个是TfidfTransformer,它用来将CountVectorizer从文本中提取的特征矩阵进行转化,另一个是TfidfVectorizer,它和CountVectorizer用法是相同的,相当于把CountVectorizer和TfidfTransformer所做的工作整合在了一起.

  在自然语言领域最常用的python工具包--NLTK.其也可以实现分词,为文本加注标签等功能,还可以进行词干提取以及词干还原.

  如果想进一步发展,可以深入了解话题建模(Topic Modeling)和文档聚类(Document Clustering).

  在深度学习领域最常用来做自然语言处理的当属word2vec库,如果有兴趣的可以深入了解.

 

文章引自 ; 《深入浅出python机器学习》