玩命加载中 . . .

哔哩哔哩课程评价指南开发经历——深度学习


背景

上一篇讲解到了抓包爬取哔哩哔哩课程的评论。接下来需要对这些评论文本进行情感分类——本质上就是一个文本二分类问题。于是使用较为熟悉的深度学习方法进行解决——搭建深度学习模型。

深度学习模型结构图

此处需要强调的是,需要获取中文分词的拼音,将中文分词和拼音均送入后续的神经网络,还需要在送入全连接层前进行拼接。

实现

第一步:制作数据集

制作数据集是一件很繁琐的事情,这里为了简化工作,暂且制作了一个由100条好评和100条差评组成的数据集。

第二步:预处理文本

由于我们只考虑中文评论文本,而很多评论中包含很多对分类结果没有意义的英文和数字,以及一些特殊字符(比如空格、换行等),因此编写一个对文本进行预处理的函数。另外预处理完成后,使用jieba对中文评论文本进行分词,剔除其中的停用词,得到最终的中文分词结果后,再获取它们的拼音,得到最终的数据集

剔除英文、数字并分词

使用正则表达式可以很方便地实现这一效果。具体参考了这篇博客:https://blog.csdn.net/Wuyeyu2001/article/details/127324156

def pre_filter(text):
    '''参考: https://blog.csdn.net/Wuyeyu2001/article/details/127324156'''
    text = re.sub('[a-zA-Z0-9]', '', text)
    text = re.sub('\W', '', text)
    return jieba.lcut(text)

剔除停用词

停用词可以理解为一些语气词、助词等实际意义较弱的词,需要在文本分类中进行去除。从网上下载已经总结出来的停用词文本文件:https://github.com/goto456/stopwords

然后进行读取,并将其中的词存储到列表中:

# 读取停用词
stop_words = []
with open('../data/stopwords/hit_stopwords.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()
    for line in lines:
        stop_words.append(line.strip())

具体的剔除停用词,可以编写一个过滤器filter,将存在与停用词表中的词过滤掉即可。

获取词汇拼音

需要使用一个强有力的包xpinyin,按照如下语句调用即可很方便地获取目标语句的拼音(带音调):

p = Pinyin()
data = "哔哩 哔哩 好用 喜欢"
pinyin_data = p.get_pinyin(data, '', tone_marks='marks')
# pinyin_data: bìlī bìlī hǎoyòng xǐhuān

项目中只需要将data替换成已经划分好的中文分词即可。

小结

综上,完整的文本预处理得到新的文本数据集的代码如下。

csv_file = "../data/dataset.csv"
content = pd.read_csv(csv_file, encoding='gbk').values
save_file = "../data/new_worddataset.csv"
save_pinyin_file = "../data/new_dataset.csv" # 最终得到的新数据集文件
p = Pinyin()

with open(save_file, "w", newline='') as f1, open(save_pinyin_file, "w", newline='', encoding='utf-8') as f2:
    writer = csv.writer(f1)
    writer.writerow(['content', 'label'])
    pinyin_writer = csv.writer(f2)
    pinyin_writer.writerow(['text', 'pinyin', 'label'])

    for sample in content:
        reply, label = sample[0], sample[1]
        reply = pre_filter(reply)
        data = list(filter(lambda w: w not in stop_words, reply))
        data = ' '.join(data)
        # 获取分词的拼音
        pinyin_data = p.get_pinyin(data, '', tone_marks='marks')
        writer.writerow([data, label])
        # 将评论文本、拼音、标签一起写入.csv文件,成为完整的一条样本
        pinyin_writer.writerow([data, pinyin_data, label])

原始的数据集长这样:

image-20230807110234144

转化后的数据集长这样:

image-20230807110326385

第三步:搭建深度学习模型

我们的深度学习模型由于需要进行文本分类,因此还需要额外一个嵌入(Embedding)层。此外还需要LSTM层和CNN层、全连接层。

嵌入

对词嵌入(word embedding)可以这样理解:计算机程序的处理对象通常是数值对象,而无法直接处理人类所使用的自然语言(字符串),比如计算机视觉中的图像处理,不也是把图像转化成一个一个的像素点数值吗?与此类似,将自然语言的“词汇”转化为可以被计算机程序所计算的数值的过程,就是所谓的词嵌入。更具体地,可以理解为,通过实现构建一个词典词汇到向量的映射,每一个词汇对应一个数学向量,即词向量),然后处理自然语言时,每每在词典中查找每一个词汇,就将其替换为其所对应的词向量。

那么重点来了,如何得到这样的一个“词典”?一旦得到它,不就可以对用户输入的自然语言进行jieba分词、查找词汇替换词向量、送入计算程序为计算机处理了?所以接下来看看构建词典的方法。

本项目中使用的技术是word2vec。也就是训练大规模的中文语料,得到一个大的词表,其中每一个词就对应着一个词向量。那么先将目光聚焦到当前构建词表的任务中,而不要去看原初的文本分类大任务。

下载中文语料库

开源的中文语料库特别多,随便到网络上下载一个即可,比如金庸的某小说也行。本项目中已经准备好了一份文本文件,其中的中文语料均已经分词完成。

调用gensim库进行训练

注意,我们的文本分类模型需要接受中文评论和它的拼音作为输入,那么也就是说,我们需要先分别训练一个中文词表和中文拼音词表。它们的训练大同小异,这里仅仅以中文词表的训练构建为例进行说明了。

导入所需要的包:gensim

from gensim.models import KeyedVectors,word2vec,Word2Vec
from gensim import utils
import jieba

创建一个语料处理类,实际上是一个可迭代对象,在其__iter__()方法中,每次产出文本文件中的一行。

class MyCorpus:
    def __init__(self, corpus_path):
        self.corpus_path = corpus_path

    def __iter__(self):
        for line in open(self.corpus_path, encoding='utf-8'):
            #对读取的句子进行简单的处理
            yield utils.simple_preprocess(line)

然后使用上面定义好的语料处理类,读取准备好的中文语料文件;将读取的结果送入Word2Vec对象,并指定词向量维度为$300$(一般称之为嵌入维度,embedding_dim)。Word2Vecgensim包中很有用的word2vec训练工具。训练完成后,将模型保存下来(这里选择保存为.vector和.bin文件,当然也可以选择保存为其他格式的模型文件,只不过,.vector模型文件支持点开预览各个词以及对应的词向量)

sentences = MyCorpus(file_path)
model = Word2Vec(sentences=sentences, vector_size=300)
model.wv.save_word2vec_format('dl/word2vec.vector')
model.wv.save_word2vec_format('dl/word2vec.bin')
创建词向量权重矩阵

训练完成后,我们就得到了基于下载的中文语料的中文词向量。为了能够更方便地使用词嵌入,我们需要用到torch提供的一个接口torch.nn.Embedding.from_pretrained()导入已经训练完毕得到的词向量权重矩阵。

下面根据训练得到的word2vec模型,创建权重矩阵。

word2vec模型文件概览

我们先导入之前保存好的模型文件word2vec.vector

word_dict = KeyedVectors.load_word2vec_format('./word2vec.vector')

然后获取全部的词,取出它们对应的词向量存入矩阵的同时,建立中文词到数值索引的映射

word_list = word_dict.index_to_key # ['一个', '可以']
vocab_size = len(word_list)
word2index = {} # 词表,中文词到数值索引的映射表
embeddings = torch.zeros(size=(vocab_size+1, embed_dim)) # 嵌入词表的权重矩阵
for idx in range(len(word_list)):
    word2index[word_list[idx]] = idx
    embeddings[idx, :] = torch.from_numpy(word_dict[word_list[idx]]) # 填充

定义嵌入层代码如下

net = torch.nn.Embedding.from_pretrained(embeddings=embeddings, freeze=True)

这里的embeddings就是上面填充得到的词向量权重文件。在嵌入层接受输入时,只需要根据之前建立的中文词导数值索引的映射,将输入的分词转化为它们对应的数值索引,然后送入嵌入层,即可转化为词向量,继续后续的程序了。


文章作者: 鹿卿
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 鹿卿 !
评论
  目录