douban_starter-checkpoint.ipynb
96.4 KB
豆瓣评分的预测
在这个项目中,我们要预测一部电影的评分,这个问题实际上就是一个分类问题。给定的输入为一段文本,输出为具体的评分。 在这个项目中,我们需要做:
- 文本的预处理,如停用词的过滤,低频词的过滤,特殊符号的过滤等
- 文本转化成向量,将使用三种方式,分别为tf-idf, word2vec以及BERT向量。
- 训练逻辑回归和朴素贝叶斯模型,并做交叉验证
- 评估模型的准确率
在具体标记为TODO
的部分填写相应的代码。
In [1]:
#导入数据处理的基础包 import numpy as np import pandas as pd #导入用于计数的包 from collections import Counter #导入tf-idf相关的包 from sklearn.feature_extraction.text import TfidfTransformer from sklearn.feature_extraction.text import CountVectorizer #导入模型评估的包 from sklearn import metrics #导入与word2vec相关的包 from gensim.models import KeyedVectors #导入与bert embedding相关的包,关于mxnet包下载的注意事项参考实验手册 from bert_embedding import BertEmbedding import mxnet #包tqdm是用来对可迭代对象执行时生成一个进度条用以监视程序运行过程 from tqdm import tqdm #导入其他一些功能包 import requests import os import re import warnings warnings.filterwarnings("ignore")
In [38]:
# 由于bert嵌入无法使用GPU进行加速,所以这里使用了比例系数K来减小数据量 K = 5000
1. 读取数据并做文本的处理
你需要完成以下几步操作:
- 去掉无用的字符如!&,可自行定义
- 中文分词
- 去掉低频词
In [3]:
#读取数据 data = pd.read_csv('data/DMSC.csv') #观察数据格式 data.head()
ID | Movie_Name_EN | Movie_Name_CN | Crawl_Date | Number | Username | Date | Star | Comment | Like | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | Avengers Age of Ultron | 复仇者联盟2 | 2017-01-22 | 1 | 然潘 | 2015-05-13 | 3 | 连奥创都知道整容要去韩国。 | 2404 |
1 | 10 | Avengers Age of Ultron | 复仇者联盟2 | 2017-01-22 | 11 | 影志 | 2015-04-30 | 4 | “一个没有黑暗面的人不值得信任。” 第二部剥去冗长的铺垫,开场即高潮、一直到结束,会有人觉... | 381 |
2 | 20 | Avengers Age of Ultron | 复仇者联盟2 | 2017-01-22 | 21 | 随时流感 | 2015-04-28 | 2 | 奥创弱爆了弱爆了弱爆了啊!!!!!! | 120 |
3 | 30 | Avengers Age of Ultron | 复仇者联盟2 | 2017-01-22 | 31 | 乌鸦火堂 | 2015-05-08 | 4 | 与第一集不同,承上启下,阴郁严肃,但也不会不好看啊,除非本来就不喜欢漫威电影。场面更加宏大... | 30 |
4 | 40 | Avengers Age of Ultron | 复仇者联盟2 | 2017-01-22 | 41 | 办公室甜心 | 2015-05-10 | 5 | 看毕,我激动地对友人说,等等奥创要来毁灭台北怎么办厚,她拍了拍我肩膀,没事,反正你买了两份... | 16 |
In [4]:
#输出数据的一些相关信息 data.info()
Out [4]:
<class 'pandas.core.frame.DataFrame'> RangeIndex: 212506 entries, 0 to 212505 Data columns (total 10 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ID 212506 non-null int64 1 Movie_Name_EN 212506 non-null object 2 Movie_Name_CN 212506 non-null object 3 Crawl_Date 212506 non-null object 4 Number 212506 non-null int64 5 Username 212496 non-null object 6 Date 212506 non-null object 7 Star 212506 non-null int64 8 Comment 212506 non-null object 9 Like 212506 non-null int64 dtypes: int64(4), object(6) memory usage: 16.2+ MB
In [5]:
#只保留数据中我们需要的两列:Comment列和Star列 data = data[['Comment','Star']] #观察新的数据的格式 data.head()
Comment | Star | |
---|---|---|
0 | 连奥创都知道整容要去韩国。 | 3 |
1 | “一个没有黑暗面的人不值得信任。” 第二部剥去冗长的铺垫,开场即高潮、一直到结束,会有人觉... | 4 |
2 | 奥创弱爆了弱爆了弱爆了啊!!!!!! | 2 |
3 | 与第一集不同,承上启下,阴郁严肃,但也不会不好看啊,除非本来就不喜欢漫威电影。场面更加宏大... | 4 |
4 | 看毕,我激动地对友人说,等等奥创要来毁灭台北怎么办厚,她拍了拍我肩膀,没事,反正你买了两份... | 5 |
In [6]:
# 这里的star代表具体的评分。但在这个项目中,我们要预测的是正面还是负面。我们把评分为1和2的看作是负面,把评分为3,4,5的作为正面 data['Star']=(data.Star/3).astype(int) data.head(20)
Comment | Star | |
---|---|---|
0 | 连奥创都知道整容要去韩国。 | 1 |
1 | “一个没有黑暗面的人不值得信任。” 第二部剥去冗长的铺垫,开场即高潮、一直到结束,会有人觉... | 1 |
2 | 奥创弱爆了弱爆了弱爆了啊!!!!!! | 0 |
3 | 与第一集不同,承上启下,阴郁严肃,但也不会不好看啊,除非本来就不喜欢漫威电影。场面更加宏大... | 1 |
4 | 看毕,我激动地对友人说,等等奥创要来毁灭台北怎么办厚,她拍了拍我肩膀,没事,反正你买了两份... | 1 |
5 | 绝逼不质疑尾灯的导演和编剧水平 | 1 |
6 | avengers1睡着1次 avengers2睡着两次。。。 | 0 |
7 | 谁再喊我看这种电影我和谁急!实在是接受无能。。。 | 0 |
8 | 超愉悦以及超满足。在历经了第一阶段比漫画更普世的设定融合之后,发展到#AoU#居然出现了不... | 1 |
9 | 观影过程中,耳边一直有一种突突突突突的声音,我还感慨电影为了让奥创给观众带来紧张感,声音上... | 1 |
10 | Long takes, no stakes. 最后大战灾难性得乱 olsen到底什么能力完... | 1 |
11 | 视觉效果的极限是视觉疲劳 | 1 |
12 | 感觉有略黑暗了点,不过还是萌点满满,但是一想到就要完结了又心碎了一地,,,, | 1 |
13 | 妇联成员都只会讲不好笑的笑话,唯一加分的是朱莉·德培 | 0 |
14 | 只算還OK的商業片。現在這類片型第一品牌就是漫威了,熱鬧打鬥大場面,人神機甲齊飛,各型超級... | 1 |
15 | 好看!好看!好看! | 1 |
16 | 难看一笔 | 0 |
17 | 6/10。第一部精准的节奏、巧妙的悬念和清楚的内心戏不见了,或许导演不想把超级英雄打造成战... | 1 |
18 | 欧洲竟然真的是最早上映啊= =法国比美国还早一周……没怎么看懂的我想找科普说明都不容易!嘛... | 1 |
19 | 我是美队的忠实脑!残!粉!!!!!!!!! | 1 |
任务1: 去掉一些无用的字符
In [7]:
# TODO1: 去掉一些无用的字符,自行定一个字符几何,并从文本中去掉 def pre_process(input_str): # input_str = re.sub('[0-9]+', 'DIG', input_str) # 去除标点符号 # input_str = re.sub(r"[{}]+".format(punc), " ", input_str) input_str = re.sub( "[0-9a-zA-Z\-\s+\.\!\/_,$%^*\(\)\+(+\"\')]+|[+——!,。?、~@#¥%……&*()<>\[\]::★◆【】《》;;=??]+", " ", input_str) # 其他非中文字符 input_str = re.sub(r"[^\u4e00-\u9fff]", " ", input_str) return input_str.strip() # 正则去除标点符号 data['comment_clean'] = data['Comment'].apply(pre_process) data.head(20)
Comment | Star | comment_clean | |
---|---|---|---|
0 | 连奥创都知道整容要去韩国。 | 1 | 连奥创都知道整容要去韩国 |
1 | “一个没有黑暗面的人不值得信任。” 第二部剥去冗长的铺垫,开场即高潮、一直到结束,会有人觉... | 1 | 一个没有黑暗面的人不值得信任 第二部剥去冗长的铺垫 开场即高潮 一直到结束 会有人觉得只... |
2 | 奥创弱爆了弱爆了弱爆了啊!!!!!! | 0 | 奥创弱爆了弱爆了弱爆了啊 |
3 | 与第一集不同,承上启下,阴郁严肃,但也不会不好看啊,除非本来就不喜欢漫威电影。场面更加宏大... | 1 | 与第一集不同 承上启下 阴郁严肃 但也不会不好看啊 除非本来就不喜欢漫威电影 场面更加宏大 ... |
4 | 看毕,我激动地对友人说,等等奥创要来毁灭台北怎么办厚,她拍了拍我肩膀,没事,反正你买了两份... | 1 | 看毕 我激动地对友人说 等等奥创要来毁灭台北怎么办厚 她拍了拍我肩膀 没事 反正你买了两份旅... |
5 | 绝逼不质疑尾灯的导演和编剧水平 | 1 | 绝逼不质疑尾灯的导演和编剧水平 |
6 | avengers1睡着1次 avengers2睡着两次。。。 | 0 | 睡着 次 睡着两次 |
7 | 谁再喊我看这种电影我和谁急!实在是接受无能。。。 | 0 | 谁再喊我看这种电影我和谁急 实在是接受无能 |
8 | 超愉悦以及超满足。在历经了第一阶段比漫画更普世的设定融合之后,发展到#AoU#居然出现了不... | 1 | 超愉悦以及超满足 在历经了第一阶段比漫画更普世的设定融合之后 发展到 居然出现了不少传统... |
9 | 观影过程中,耳边一直有一种突突突突突的声音,我还感慨电影为了让奥创给观众带来紧张感,声音上... | 1 | 观影过程中 耳边一直有一种突突突突突的声音 我还感慨电影为了让奥创给观众带来紧张感 声音上真... |
10 | Long takes, no stakes. 最后大战灾难性得乱 olsen到底什么能力完... | 1 | 最后大战灾难性得乱 到底什么能力完全没明白 是巴菲里的 其实剧本没那么差 美国例外论的主... |
11 | 视觉效果的极限是视觉疲劳 | 1 | 视觉效果的极限是视觉疲劳 |
12 | 感觉有略黑暗了点,不过还是萌点满满,但是一想到就要完结了又心碎了一地,,,, | 1 | 感觉有略黑暗了点 不过还是萌点满满 但是一想到就要完结了又心碎了一地 |
13 | 妇联成员都只会讲不好笑的笑话,唯一加分的是朱莉·德培 | 0 | 妇联成员都只会讲不好笑的笑话 唯一加分的是朱莉 德培 |
14 | 只算還OK的商業片。現在這類片型第一品牌就是漫威了,熱鬧打鬥大場面,人神機甲齊飛,各型超級... | 1 | 只算還 的商業片 現在這類片型第一品牌就是漫威了 熱鬧打鬥大場面 人神機甲齊飛 各型超級英雄... |
15 | 好看!好看!好看! | 1 | 好看 好看 好看 |
16 | 难看一笔 | 0 | 难看一笔 |
17 | 6/10。第一部精准的节奏、巧妙的悬念和清楚的内心戏不见了,或许导演不想把超级英雄打造成战... | 1 | 第一部精准的节奏 巧妙的悬念和清楚的内心戏不见了 或许导演不想把超级英雄打造成战斗机器 所以... |
18 | 欧洲竟然真的是最早上映啊= =法国比美国还早一周……没怎么看懂的我想找科普说明都不容易!嘛... | 1 | 欧洲竟然真的是最早上映啊 法国比美国还早一周 没怎么看懂的我想找科普说明都不容易 嘛 我... |
19 | 我是美队的忠实脑!残!粉!!!!!!!!! | 1 | 我是美队的忠实脑 残 粉 |
任务2:使用结巴分词对文本做分词
In [8]:
# TODO2: 导入中文分词包jieba, 并用jieba对原始文本做分词 import jieba def comment_cut(content): # TODO: 使用结巴完成对每一个comment的分词 # 分词并过滤空字符串 return ' '.join([w for w in jieba.lcut(content.strip()) if len(w) > 0]) # 输出进度条 tqdm.pandas(desc='apply') data['comment_processed'] = data['comment_clean'].progress_apply(comment_cut) # 观察新的数据的格式 data.head()
Out [8]:
apply: 0%| | 0/212506 [00:00<?, ?it/s]Building prefix dict from the default dictionary ... Loading model from cache C:\Users\avaws\AppData\Local\Temp\jieba.cache Loading model cost 0.630 seconds. Prefix dict has been built successfully. apply: 100%|█████████████████████████████████████████████████████████████████| 212506/212506 [00:35<00:00, 5973.38it/s]
任务3:设定停用词并去掉停用词
In [9]:
# TODO3: 设定停用词并从文本中去掉停用词 # 下载中文停用词表至data/stopWord.json中,下载地址:https://github.com/goto456/stopwords/ if not os.path.exists('./data/stopWord.json'): stopWord = requests.get("https://raw.githubusercontent.com/goto456/stopwords/master/cn_stopwords.txt") with open("./data/stopWord.json", "wb") as f: f.write(stopWord.content) # 读取下载的停用词表,并保存在列表中 with open("./data/stopWord.json","r", encoding="utf-8") as f: stopWords = f.read().split("\n") # 去除停用词 def rm_stop_word(input_str): # your code, remove stop words # TODO return [w for w in input_str.split() if w not in stopWords] #这行代码中.progress_apply()函数的作用等同于.apply()函数的作用,只是写成.progress_apply()函数才能被tqdm包监控从而输出进度条。 data['comment_processed'] = data['comment_processed'].progress_apply(rm_stop_word)
Out [9]:
apply: 100%|█████████████████████████████████████████████████████████████████| 212506/212506 [00:31<00:00, 6837.77it/s]
In [10]:
data.head(20)
Comment | Star | comment_clean | comment_processed | |
---|---|---|---|---|
0 | 连奥创都知道整容要去韩国。 | 1 | 连奥创都知道整容要去韩国 | [奥创, 知道, 整容, 韩国] |
1 | “一个没有黑暗面的人不值得信任。” 第二部剥去冗长的铺垫,开场即高潮、一直到结束,会有人觉... | 1 | 一个没有黑暗面的人不值得信任 第二部剥去冗长的铺垫 开场即高潮 一直到结束 会有人觉得只... | [一个, 没有, 黑暗面, 值得, 信任, 第二部, 剥去, 冗长, 铺垫, 开场, 高潮,... |
2 | 奥创弱爆了弱爆了弱爆了啊!!!!!! | 0 | 奥创弱爆了弱爆了弱爆了啊 | [奥创, 弱, 爆, 弱, 爆, 弱, 爆] |
3 | 与第一集不同,承上启下,阴郁严肃,但也不会不好看啊,除非本来就不喜欢漫威电影。场面更加宏大... | 1 | 与第一集不同 承上启下 阴郁严肃 但也不会不好看啊 除非本来就不喜欢漫威电影 场面更加宏大 ... | [第一集, 不同, 承上启下, 阴郁, 严肃, 不会, 好看, 本来, 喜欢, 漫威, 电影... |
4 | 看毕,我激动地对友人说,等等奥创要来毁灭台北怎么办厚,她拍了拍我肩膀,没事,反正你买了两份... | 1 | 看毕 我激动地对友人说 等等奥创要来毁灭台北怎么办厚 她拍了拍我肩膀 没事 反正你买了两份旅... | [看毕, 激动, 友人, 说, 奥创, 毁灭, 台北, 厚, 拍了拍, 肩膀, 没事, 反正... |
5 | 绝逼不质疑尾灯的导演和编剧水平 | 1 | 绝逼不质疑尾灯的导演和编剧水平 | [绝逼, 质疑, 尾灯, 导演, 编剧, 水平] |
6 | avengers1睡着1次 avengers2睡着两次。。。 | 0 | 睡着 次 睡着两次 | [睡着, 次, 睡着, 两次] |
7 | 谁再喊我看这种电影我和谁急!实在是接受无能。。。 | 0 | 谁再喊我看这种电影我和谁急 实在是接受无能 | [喊, 这种, 电影, 急, 实在, 接受, 无能] |
8 | 超愉悦以及超满足。在历经了第一阶段比漫画更普世的设定融合之后,发展到#AoU#居然出现了不... | 1 | 超愉悦以及超满足 在历经了第一阶段比漫画更普世的设定融合之后 发展到 居然出现了不少传统... | [超, 愉悦, 超, 满足, 历经, 第一阶段, 漫画, 更普世, 设定, 融合, 之后, ... |
9 | 观影过程中,耳边一直有一种突突突突突的声音,我还感慨电影为了让奥创给观众带来紧张感,声音上... | 1 | 观影过程中 耳边一直有一种突突突突突的声音 我还感慨电影为了让奥创给观众带来紧张感 声音上真... | [观影, 过程, 中, 耳边, 一直, 一种, 突突突, 突突, 声音, 感慨, 电影, 奥... |
10 | Long takes, no stakes. 最后大战灾难性得乱 olsen到底什么能力完... | 1 | 最后大战灾难性得乱 到底什么能力完全没明白 是巴菲里的 其实剧本没那么差 美国例外论的主... | [最后, 大战, 灾难性, 得乱, 到底, 能力, 完全, 没, 明白, 巴菲, 里, 其实... |
11 | 视觉效果的极限是视觉疲劳 | 1 | 视觉效果的极限是视觉疲劳 | [视觉效果, 极限, 视觉, 疲劳] |
12 | 感觉有略黑暗了点,不过还是萌点满满,但是一想到就要完结了又心碎了一地,,,, | 1 | 感觉有略黑暗了点 不过还是萌点满满 但是一想到就要完结了又心碎了一地 | [感觉, 有略, 黑暗, 点, 萌点, 满满, 想到, 完结, 心碎, 一地] |
13 | 妇联成员都只会讲不好笑的笑话,唯一加分的是朱莉·德培 | 0 | 妇联成员都只会讲不好笑的笑话 唯一加分的是朱莉 德培 | [妇联, 成员, 只会, 讲, 不好, 笑, 笑话, 唯一, 加分, 朱莉, 德培] |
14 | 只算還OK的商業片。現在這類片型第一品牌就是漫威了,熱鬧打鬥大場面,人神機甲齊飛,各型超級... | 1 | 只算還 的商業片 現在這類片型第一品牌就是漫威了 熱鬧打鬥大場面 人神機甲齊飛 各型超級英雄... | [只算還, 商業片, 現在, 這類, 片型, 第一, 品牌, 漫威, 熱鬧, 打鬥大場, 面... |
15 | 好看!好看!好看! | 1 | 好看 好看 好看 | [好看, 好看, 好看] |
16 | 难看一笔 | 0 | 难看一笔 | [难看, 一笔] |
17 | 6/10。第一部精准的节奏、巧妙的悬念和清楚的内心戏不见了,或许导演不想把超级英雄打造成战... | 1 | 第一部精准的节奏 巧妙的悬念和清楚的内心戏不见了 或许导演不想把超级英雄打造成战斗机器 所以... | [第一部, 精准, 节奏, 巧妙, 悬念, 清楚, 内心, 戏, 不见, 或许, 导演, 不... |
18 | 欧洲竟然真的是最早上映啊= =法国比美国还早一周……没怎么看懂的我想找科普说明都不容易!嘛... | 1 | 欧洲竟然真的是最早上映啊 法国比美国还早一周 没怎么看懂的我想找科普说明都不容易 嘛 我... | [欧洲, 竟然, 真的, 最早, 上映, 法国, 美国, 早, 一周, 没, 懂, 想, 找... |
19 | 我是美队的忠实脑!残!粉!!!!!!!!! | 1 | 我是美队的忠实脑 残 粉 | [美队, 忠实, 脑, 残, 粉] |
任务4:去掉低频词,出现次数少于10次的词去掉
In [11]:
# TODO4: 去除低频词, 去掉词频小于10的单词,并把结果存放在data['comment_processed']里 word_counter = Counter([w for s in data['comment_processed'].values for w in s]) def rm_low_frequency_words(word_list): return [w for w in word_list if word_counter[w] >= 10] data['comment_processed'] = data['comment_processed'].progress_apply(rm_low_frequency_words) data['comment_processed_str'] = data['comment_processed'].apply(lambda x: ' '.join(x)) data.head(10)
Out [11]:
apply: 100%|███████████████████████████████████████████████████████████████| 212506/212506 [00:00<00:00, 227439.46it/s]
2. 把文本分为训练集和测试集
选择语料库中的20%作为测试数据,剩下的作为训练数据
In [12]:
# TODO5: 把数据分为训练集和测试集. comments_train(list)保存用于训练的文本,comments_test(list)保存用于测试的文本。 y_train, y_test是对应的标签(0、1) from sklearn.model_selection import train_test_split test_ratio = 0.2 # https://machinelearningmastery.com/train-test-split-for-evaluating-machine-learning-algorithms/ src_training, src_testing = train_test_split(data, test_size=test_ratio, stratify=data['Star']) comments_train, comments_test = src_training['comment_processed_str'].values, src_testing['comment_processed_str'].values y_train, y_test = src_training['Star'].values, src_testing['Star'].values
3. 把文本转换成向量的形式
在这个部分我们会采用三种不同的方式:
- 使用tf-idf向量
- 使用word2vec
- 使用bert向量
转换成向量之后,我们接着做模型的训练
任务6:把文本转换成tf-idf向量
In [13]:
# TODO6: 把训练文本和测试文本转换成tf-idf向量。使用sklearn的feature_extraction.text.TfidfTransformer模块 # 请留意fit_transform和transform之间的区别。 常见的错误是在训练集和测试集上都使用 fit_transform,需要避免! # 另外,可以留意一下结果是否为稀疏矩阵 from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_extraction.text import TfidfTransformer count_vectorizer = CountVectorizer(token_pattern=r"(?u)\b\w+\b") tfidf_transformer = TfidfTransformer() word_count_train = count_vectorizer.fit_transform(comments_train) tfidf_train = tfidf_transformer.fit_transform(word_count_train) word_count_test = count_vectorizer.transform(comments_test) tfidf_test = tfidf_transformer.transform(word_count_test) print(tfidf_train.shape, tfidf_test.shape)
Out [13]:
(170004, 15763) (42502, 15763)
任务7:把文本转换成word2vec向量
In [14]:
# 由于训练出一个高效的word2vec词向量往往需要非常大的语料库与计算资源,所以我们通常不自己训练Wordvec词向量,而直接使用网上开源的已训练好的词向量。 # data/sgns.zhihu.word是从https://github.com/Embedding/Chinese-Word-Vectors下载到的预训练好的中文词向量文件 # 使用KeyedVectors.load_word2vec_format()函数加载预训练好的词向量文件 model = KeyedVectors.load_word2vec_format('data/sgns.zhihu.word')
In [15]:
#预训练词向量使用举例 model['我们']
Out [15]:
array([-0.200708, 0.188213, -0.20941 , 0.048857, 0.116663, 0.547244, -0.449441, -0.177554, 0.123547, 0.161301, -0.20861 , 0.429821, -0.429595, -0.45094 , 0.190053, 0.175438, 0.066855, -0.157346, 0.134905, -0.128076, 0.111503, -0.03149 , -0.347445, -0.231517, 0.212383, 0.29857 , 0.167368, -0.064022, -0.048241, 0.109434, -0.156835, -0.558394, -0.005307, 0.127788, -0.053521, -0.154787, -0.048875, 0.109031, 0.160019, 0.273365, -0.023131, -0.257962, -0.051904, 0.103058, 0.019103, 0.210418, -0.12053 , 0.084021, 0.085243, -0.406479, -0.285062, -0.229883, -0.125173, -0.141597, -0.018101, -0.215311, -0.091788, 0.315358, 0.242912, 0.013785, -0.078914, 0.158206, 0.180421, -0.050306, -0.008539, -0.201157, 0.047753, 0.293518, 0.340344, 0.098132, 0.356952, 0.189959, -0.107122, -0.176698, 0.011044, 0.131703, 0.134601, -0.078891, 0.217989, 0.05074 , 0.063365, 0.30178 , 0.161369, 0.157998, -0.128195, -0.060345, 0.047446, -0.146161, 0.005427, -0.06684 , 0.056229, -0.04922 , -0.122368, 0.181634, 0.180599, 0.026725, -0.383503, -0.10855 , 0.06524 , -0.095767, 0.08362 , 0.287755, -0.325982, -0.026982, 0.147817, 0.041374, 0.342181, -0.010403, -0.082642, 0.124128, -0.104747, 0.002654, -0.086981, -0.044065, -0.085694, -0.020068, -0.125195, -0.154542, -0.030115, 0.100488, 0.081022, 0.06612 , 0.088058, -0.102289, -0.061927, -0.054882, 0.510755, -0.154545, 0.029478, -0.191885, -0.048633, -0.218267, -0.14659 , -0.028195, 0.223698, 0.101008, 0.100562, -0.237451, 0.492519, -0.163208, -0.466598, 0.041121, 0.153394, 0.066931, 0.428429, 0.238117, 0.188347, 0.290581, 0.147405, -0.222624, 0.336171, -0.128802, 0.032038, 0.036617, 0.042459, 0.031089, 0.092689, 0.092509, -0.206014, -0.093757, -0.079919, 0.052213, 0.176261, 0.030587, -0.222407, -0.293368, -0.210982, 0.086169, -0.41054 , 0.168664, -0.110555, 0.104398, 0.131111, 0.034967, -0.240558, 0.050963, 0.002297, -0.231932, 0.138751, -0.162152, 0.128286, 0.11232 , 0.085235, 0.16869 , 0.072754, 0.004705, -0.175828, -0.082598, -0.245999, 0.103419, 0.357173, -0.05588 , 0.030934, -0.13984 , 0.011164, -0.277783, -0.168691, -0.223155, -0.203391, -0.015567, 0.161146, -0.110572, -0.06779 , -0.006586, -0.039414, 0.245169, -0.182014, 0.38548 , 0.039947, 0.36978 , 0.167039, -0.055724, 0.051462, 0.044205, -0.255853, -0.194969, -0.215543, 0.367193, -0.268322, 0.048425, 0.181398, 0.203609, 0.04321 , -0.280908, 0.215055, -0.410717, 0.209178, 0.365696, -0.26421 , 0.008008, -0.167048, 0.07082 , 0.148507, -0.121757, -0.227046, -0.161108, -0.084349, 0.173502, 0.07519 , -0.203567, 0.151776, -0.21104 , -0.334659, 0.090743, 0.049097, 0.080783, -0.062416, -0.089825, 0.230757, -0.065472, 0.313976, 0.096314, -0.145926, 0.146772, -0.007169, -0.041627, -0.050497, -0.34267 , -0.144144, -0.140267, 0.000677, -0.114036, -0.017044, -0.030107, -0.098467, -0.233114, 0.103173, 0.093112, -0.11863 , 0.086859, 0.300346, 0.146062, -0.173922, 0.162061, 0.143895, -0.158726, -0.123311, 0.166061, -0.196121, 0.207249, 0.053585, 0.025314, -0.24309 , -0.074694, -0.238774, -0.056441, -0.099747, -0.271508, 0.212461, 0.189918, 0.162701, -0.154819, 0.235821, -0.131372, -0.052284, 0.101817, 0.088172, 0.107883, 0.020072, 0.188443], dtype=float32)
In [16]:
vocabulary = model.vocab def word_vec_averaging(words, dim=300): """ Average all words vectors in one sentence. :param words: input sentence :param dim: 'size' of model :return: the averaged word vectors as the vector for the sentence """ vec_mean = np.zeros((dim,), dtype=np.float32) word_num = 0 first_dim_sum = 0 for word in words: print(f'Word is: {word}') if word in vocabulary: word_num += 1 vec_mean = np.add(vec_mean, model[word]) first_dim_sum += model[word][0] print(f'in vocab with first dimension: {model[word][0]} and first_dim_sum is: {first_dim_sum}') else: print('not in vocab') if word_num > 0: vec_mean = np.divide(vec_mean, word_num) print(f'word_num is: {word_num}, first dim average is: {first_dim_sum / word_num}') return vec_mean one_sample = comments_train[100] word_vec_averaging(one_sample.split())
Out [16]:
Word is: 太 in vocab with first dimension: 0.014244000427424908 and first_dim_sum is: 0.014244000427424908 Word is: 羡慕 in vocab with first dimension: -0.15045799314975739 and first_dim_sum is: -0.13621399272233248 Word is: 睡醒 in vocab with first dimension: -0.24956999719142914 and first_dim_sum is: -0.3857839899137616 Word is: 自带 in vocab with first dimension: -0.011017999611794949 and first_dim_sum is: -0.39680198952555656 Word is: 眼线 in vocab with first dimension: 0.5926219820976257 and first_dim_sum is: 0.19581999257206917 Word is: 功能 in vocab with first dimension: -0.23343400657176971 and first_dim_sum is: -0.037614013999700546 word_num is: 6, first dim average is: -0.006269002333283424
In [17]:
comments_train[3]
Out [17]:
'三星 韩寒 朴树 片儿 到底 情况 影评 过来 一星'
In [18]:
vocabulary = model.vocab def word_vec_averaging(words, dim=300): """ Average all words vectors in one sentence. :param words: input sentence :param dim: 'size' of model :return: the averaged word vectors as the vector for the sentence """ vec_mean = np.zeros((dim,), dtype=np.float32) word_num = 0 first_dim_sum = 0 for word in words: if word in vocabulary: word_num += 1 vec_mean = np.add(vec_mean, model[word]) first_dim_sum += model[word][0] if word_num > 0: vec_mean = np.divide(vec_mean, word_num) return vec_mean word2vec_train = np.array([word_vec_averaging(s.split()) for s in comments_train]) word2vec_test = np.array([word_vec_averaging(s.split()) for s in comments_test]) print(word2vec_train.shape, word2vec_test.shape)
Out [18]:
(170004, 300) (42502, 300)
任务8:把文本转换成bert向量
In [39]:
# 导入gpu版本的bert embedding预训练的模型。 # 若没有gpu,则ctx可使用其默认值cpu(0)。但使用cpu会使程序运行的时间变得非常慢 # 若之前没有下载过bert embedding预训练的模型,执行此句时会花费一些时间来下载预训练的模型 ctx = mxnet.cpu() embedding = BertEmbedding(ctx=ctx) # TODO8: 跟word2vec一样,计算出训练文本和测试文本的向量,仍然采用单词向量的平均。 def bert_embedding_averaging(sentence): """返回sentence bert 句向量""" tokens, token_embeddings = embedding([sentence])[0] return np.mean(np.array(token_embeddings), axis=0).astype(np.float32) bert_train = np.array([bert_embedding_averaging(s) for s in comments_train[:len(comments_train)//K]]) bert_test = np.array([bert_embedding_averaging(s) for s in comments_test[:len(comments_test)//K]]) print (bert_train.shape, bert_test.shape)
Out [39]:
(34, 768) (8, 768)
In [40]:
print (tfidf_train.shape, tfidf_test.shape) print (word2vec_train.shape, word2vec_test.shape) print (bert_train.shape, bert_test.shape)
Out [40]:
(170004, 15763) (42502, 15763) (170004, 300) (42502, 300) (34, 768) (8, 768)
4. 训练模型以及评估
对如上三种不同的向量表示法,分别训练逻辑回归模型,需要做:
- 搭建模型
- 训练模型(并做交叉验证)
- 输出最好的结果
In [21]:
# 导入逻辑回归的包 from sklearn.linear_model import LogisticRegression
任务9:使用tf-idf,并结合逻辑回归训练模型
In [46]:
# TODO9: 使用tf-idf + 逻辑回归训练模型,需要用gridsearchCV做交叉验证,并选择最好的超参数 clf = LogisticRegression() from sklearn.model_selection import GridSearchCV search_grid = { 'C': [0.01, 1, 10, 100], 'class_weight': [None, 'balanced'] } grid_search = GridSearchCV(estimator = clf, param_grid = search_grid, cv = 55, n_jobs=-1, scoring='accuracy') grid_result = grid_search.fit(tfidf_train, y_train) print(f'Best parameters: {grid_result.best_params_}') clf.fit(tfidf_train, y_train) tf_idf_y_pred = clf.predict(tfidf_test) print('TF-IDF LR test accuracy %s' % metrics.accuracy_score(y_test, tf_idf_y_pred)) #逻辑回归模型在测试集上的F1_Score print('TF-IDF LR test F1_score %s' % metrics.f1_score(y_test, tf_idf_y_pred,average="macro"))
Out [46]:
Best parameters: {'C': 1, 'class_weight': None} TF-IDF LR test accuracy 0.8766175709378382 TF-IDF LR test F1_score 0.7365217068731438
任务10:使用word2vec,并结合逻辑回归训练模型
In [45]:
# TODO10: 使用word2vec + 逻辑回归训练模型,需要用gridsearchCV做交叉验证,并选择最好的超参数 clf = LogisticRegression() from sklearn.model_selection import GridSearchCV search_grid = { 'C': [0.01, 1, 10, 100], 'class_weight': [None, 'balanced'] } grid_search = GridSearchCV(estimator = clf, param_grid = search_grid, cv = 5, n_jobs=-1, scoring='accuracy') grid_result = grid_search.fit(word2vec_train, y_train) print(f'Best parameters: {grid_result.best_params_}') clf.fit(word2vec_train, y_train) word2vec_y_pred = clf.predict(word2vec_test) print('Word2vec LR test accuracy %s' % metrics.accuracy_score(y_test, word2vec_y_pred)) #逻辑回归模型在测试集上的F1_Score print('Word2vec LR test F1_score %s' % metrics.f1_score(y_test, word2vec_y_pred,average="macro"))
Out [45]:
Best parameters: {'C': 100, 'class_weight': None} Word2vec LR test accuracy 0.848618888522893 Word2vec LR test F1_score 0.6377919282595982
任务11:使用bert,并结合逻辑回归训练模型
In [47]:
# TODO11: 使用bert + 逻辑回归训练模型,需要用gridsearchCV做交叉验证,并选择最好的超参数 clf = LogisticRegression() from sklearn.model_selection import GridSearchCV search_grid = { 'C': [0.01, 1, 10, 100], 'class_weight': [None, 'balanced'] } grid_search = GridSearchCV(estimator = clf, param_grid = search_grid, cv = 5, n_jobs=-1, scoring='accuracy') grid_result = grid_search.fit(bert_train, y_train[:len(y_train)//K]) print(f'Best parameters: {grid_result.best_params_}') clf.fit(bert_train, y_train[:len(y_train)//K]) bert_y_pred = lr.predict(bert_test) print('Bert LR test accuracy %s' % metrics.accuracy_score(y_test[:len(y_test)//K], bert_y_pred)) #逻辑回归模型在测试集上的F1_Score print('Bert LR test F1_score %s' % metrics.f1_score(y_test[:len(y_test)//K], bert_y_pred,average="macro"))
Out [47]:
Best parameters: {'C': 0.01, 'class_weight': None} Bert LR test accuracy 0.625 Bert LR test F1_score 0.38461538461538464
任务12:对于以上结果请做一下简单的总结,按照1,2,3,4提取几个关键点,包括:
- 结果说明什么问题?
- 接下来如何提高?
1.预训练的词向量和bert模型的好坏对最终结果产生直接影响。如果预训练语料和下游任务不匹配,效果可能会很差。 2.词向量的维度对结果也会产生影响,但是维度并不是越大越好。维度越大,可能会导致噪声变大。 3.本次作业使用的库都比较老,导致在我自己机器声运行是无法使用GPU加速。所以训练bert向量时值用了一小部分数据。 4.对于bert来说使用finetune方法会有所提升。