涉及:
- 文本数据的特征提取
- 中文文本的分词方法
- 用n-Garm模型优化文本数据
- 使用tf-idf模型改善特征提取
- 删除停用词
1.使用CountVectorizer对文本进行特征提取
前面,用来展示的数据特征分为:
- 用来表示数值的连续特征
- 表示样本所在分类的类型特征
第三种数据类型:文本数据
文本数据在计算机中往往被存储为字符串类型(String)
中文的处理相比较英文很难,因为在一个句子中,中文的词与词不像英文有空格作为分界线——处理中文时,先进行分词处理
如句子“The quick brown fos jumps over a lazy dog”:
#导入向量化工具CountVectorizer from sklearn.feature_extraction.text import CountVectorizer vect = CountVectorizer() #拟合文本数据 en = ['The quick brown fos jumps over a lazy dog'] vect.fit(en) print('单词数:',len(vect.vocabulary_)) print('分词:',vect.vocabulary_)
单词数: 8 分词: {'the': 7, 'quick': 6, 'brown': 0, 'fos': 2, 'jumps': 3, 'over': 5, 'lazy': 4, 'dog': 1}
【结果分析】
'a' 是冠词,没被程序作为一个单词
中文:
#使用中文文本进行实验 cn = ['那只敏捷的棕色狐狸跳过了一只懒惰的狗'] #拟合中文数据 vect.fit(cn) print('单词数:',len(vect.vocabulary_)) print('分词:',vect.vocabulary_)
单词数: 1 分词: {'那只敏捷的棕色狐狸跳过了一只懒惰的狗': 0}
【结果分析】
程序无法对中文语句进行分词(没有空格)
2.使用分词工具对中文文本进行分词
pip install jieba 安装结巴分词
#导入结巴分词 import jieba #对中文文本分词 cn = jieba.cut('那只敏捷的棕色狐狸跳过了一只懒惰的狗') #实验空格‘ ’作为词与词的分界线 cn = [' '.join(cn)] print(cn)
【结果分析】
红底黑字是“结巴分词”导入词典和建立模型的信息
分词完成,接着进行特征提取:
#使用CountVectorizer对中文文本进行向量化 vect.fit(cn) print('单词数:',len(vect.vocabulary_)) print('分词:',vect.vocabulary_)
单词数: 6 分词: {'敏捷': 2, '棕色': 3, '狐狸': 4, '跳过': 5, '一只': 0, '懒惰': 1}
【结果分析】
CountVectorizer已经可以从中文文本中提取若干个整型数值,并且生成了一个字典
接下来,使用这个字典将文本的特征表达出来,以便训练模型:
3.使用词袋模型将文本数据转为数组
上面的实验中,CountVectorizer给每个词编码为0-5的整型数,结果这样的处理,可以用一个稀疏矩阵对这个文本数据进行表示
#定义词袋模型 bag_of_words = vect.transform(cn) #打印词袋模型中的数据特征 print('转化为词袋的特征:',repr(bag_of_words))
转化为词袋的特征: <1x6 sparse matrix of type '<class 'numpy.int64'>' with 6 stored elements in Compressed Sparse Row format>
【结果分析】
原来的语句,被转化成一个1行16列的稀疏矩阵,类型为64位整型数值,其中有6个元素
6个元素都是什么?
#打印词袋模型的密度表达 print(bag_of_words.toarray())
[[1 1 1 1 1 1]]
换一句话看看结果有啥不同:
#输入新的中文文本 cn_1 = jieba.cut('懒惰的狐狸不如敏捷的狐狸敏捷,敏捷的狐狸不如懒惰的狐狸懒惰') cn2 = [' '.join(cn_1)] print(cn2)
['懒惰 的 狐狸 不如 敏捷 的 狐狸 敏捷 , 敏捷 的 狐狸 不如 懒惰 的 狐狸 懒惰']
#建立新的词袋模型 new_bag = vect.transform(cn2) #打印词袋中的数据特征 print('转化为词袋的特征:',repr(new_bag)) #打印词袋的密度表达 print(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算法改善词袋模型
词袋模型劣势——把句子看作单词的简单集合,忽略单词的顺序
比如:
#随便写 joke = jieba.cut('道士看见和尚吻了尼姑的嘴唇') joke = [' '.join(joke)] #转化为向量 vect.fit(joke) joke_feature = vect.transform(joke) #打印文本数据特征 print(joke_feature.toarray())
[[1 1 1 1 1]]
顺序打乱:
#将刚才的文本打乱 joke2 = jieba.cut('尼姑看见道士的嘴唇亲吻了和尚') joke2 = [' '.join(joke2)] #转化为向量 vect.fit(joke2) joke2_feature = vect.transform(joke2) #打印文本数据特征 print(joke2_feature.toarray())
[[1 1 1 1 1 1]]
【结果分析】
对于机器来说,意思一模一样
在CountVectorize 中调节n-Gram函数:
#修改CountVectorizer的ngram参数 vect = CountVectorizer(ngram_range=(2,2)) #重新进行文本数据的特征提取 cv = vect.fit(joke) joke_feature = cv.transform(joke) print('调整ngram参数后的词典:',cv.get_feature_names()) print('新的特征表达:',joke_feature.toarray())
调整ngram参数后的词典: ['和尚 尼姑', '尼姑 嘴唇', '看见 和尚', '道士 看见'] 新的特征表达: [[1 1 1 1]]
【结果分析】
将CountVectorizer的ngram_range参数调节为(2,2),意思是,进行组合的单词数量下线是2,上线也是2【即限制CountVectorizer将句子中相邻两个单词进行组合】
试试另一句:
#调整文本顺序 joke2 = jieba.cut('尼姑看见道士的嘴唇亲吻了和尚') joke2 = [' '.join(joke2)] #转化为向量 #vect.fit(joke2) //这句书上没有,不知道是否必须 joke2_feature = vect.transform(joke2) #打印文本数据特征 print(joke2_feature.toarray())
[[0 0 0 0]]
2.使用tf-idf模型对文本数据进行处理
使用tf-idf模型来进行文本特征提取的类,称为TfidfVectorizer
tf-idf全称为 词频-逆向文件频率
tf-idf是一种用来评估某个词对于一个语料库中某一份文件的重要程度
如果某个词在某个文件中出现的次数非常高,但在其他文件中出现的次数非常少,那么tf-idf就会认为这个词可以很好的将文件进行区分,重要程度会较高
【注意】tf-idf公式有很多变体
介绍TfidVectorizer用法和CountVectorizer的区别:
下载数据集 http://ai.stanford.edu/~amaas/data/sentiment
载入影评数据集:
!tree C:UsersDELLDesktopaclImdb
使用sklearn载入这些文本数据:
#导入文件载入工具 from sklearn.datasets import load_files #定义训练集 train_set = load_files('C:/Users/DELL/Desktop/aclImdb/train') X_train,y_train = train_set.data,train_set.target print('训练集文件数量:',len(X_train)) print('随机选一个',X_train[22])
有些评论有<br />的符号,用空格替换掉,避免影响模型
#将文本的<br />去掉 X_train = [doc.replace(b'<br />',b' ') for doc in X_train]
再载入数据集:
#载入测试集 test = load_files('C:/Users/DELL/Desktop/aclImdb/test') X_test,y_test = test.data,test.target X_test = [doc.replace(b'<br />',b' ') for doc in X_test] #测试集文件数量 len(X_test)
25000
对文本数据进行特征提取:
使用CountVectorizer
#用CountVectorize拟合训练数据 vect = CountVectorizer().fit(X_train) #将文本转化为向量 X_train_vect = vect.transform(X_train) print('训练集样本特征数量:',len(vect.get_feature_names())) print('最后10个训练集样本特征:',vect.get_feature_names()[-10:])
训练集样本特征数量: 124255 最后10个训练集样本特征: ['üvegtigris', 'üwe', 'ÿou', 'ıslam', 'ōtomo', 'şey', 'дом', 'книги', '色戒', 'rock']
使用有监督学习算法进行交叉验证评分:
看看模型能否较好的拟合训练集
#导入线性SVC分类模型 from sklearn.svm import LinearSVC #导入交叉验证工具 from sklearn.model_selection import cross_val_score #使用交叉验证对模型评分 scores = cross_val_score(LinearSVC(),X_train_vect,y_train) print('模型平均分:',scores.mean())
。。。。。。数据集太大,十分钟还没好。。。。。。就用书上截图了.。。。
0.778
泛化到测试集:
#把测试数据集转化为向量 X_test_vect = vect.transform(X_test) #使用线性SVC拟合训练集 clf = LinearSVC().fit(X_train_vect,y_train) print('测试集得分:',clf.score(X_test_vect,y_test))
0.58
接下来用tf-idf算法处理数据:
#导入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('未处理的特征:',X_train_vect[:5,:5].toarray()) print('经tfidf处理的特征:',X_train_tfidf[:5,:5].toarray())
结果处理后的数据集训练的模型评分:
#重新训练线性SVC模型 clf = LinearSVC().fit(X_train_tfidf,y_train) #使用新的数据进行交叉验证 scores2 = cross_val_score(LinearSVC(),X_train_tfidf,y_train) print('经过tfidf处理的训练集交叉验证得分:',scores.mean()) print('经过tfidf处理的测试集得分:',clf.score(X_test_tfidf,y_test))
【结果分析】
继续对模型继续改进——删除“停用词”
3.删除停用词
停用词,指在文本处理过程中被筛选出去的,出现频率高,但无意义,比如各种语气词、连词、介词
目前没有通用的定义“停用词”的规则或工具
常见方法:
- 统计文本中出现频率最高的,然后把他们作为“停用词”
- 使用现有的停用词表
载入sklearn内置的停用词表:
#导入内置的停用词库 from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS #打印停用词个数 print(len(ENGLISH_STOP_WORDS)) #打印前20和后20个 print(list(ENGLISH_STOP_WORDS)[:20],list(ENGLISH_STOP_WORDS)[:-20:])
在上方的影视评语数据集继续停用词删除:
#导入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) clf.fit(X_train_tfidf,y_train) #将测试集转化为向量 X_test_tfidf = tfidf.transform(X_test) print('去掉停用词后训练集交叉验证平均分:',scores3.mean()) print('去掉停用词后测试集模型得分:',clf.score(X_test_tfidf,y_test))
【结果分析】
去掉停用词,可以让机器学习模型更好的拟合文本数据,并提高模型的泛化能力