目录

1、思路

2、基于paddle的ERINE模型进行迁移学习训练

3、分步实现

3.1 获取数据

(1)数据解压

(2)将文本转成变量,这里为了好计算,我只选了新闻标题做文本分类

3.2 中文分词

基于jieba的分词

基于paddlehub的lac分词

3.3 创建语料字典,并将语料序列化

基于语料本身的序列化

基于word2vec或已有字典的序列化

3.4 词嵌入

基于embedding的词嵌入

基于word2vec的词嵌入

3.5 构建分类模型

基于LSTM的分类模型:


实现环境:AI studio

1、思路

文本分类任务步骤通常是

  • 文本预处理

文本预处理的方法很多,类似于词性分析,句法分析,命名实体识别等,在进行文本分类之前,需要将文本进行结构化,常见的方法有one-hot,n_gram,word2vec等,与英文不同(可以简单用空格和符号进行分词),中文是比较紧密连接的,结构化之前需要对文本进行分词,如jieba分词,此外还需要将分词之后的语料转化为ID序列,然后进行词嵌入(word Embedding)。

  • DL特征提取和分类模型

适合文本的dl model有RNN,LSTM,GRU等。

  • 预测

训练完,然后对数据进行预测。

2、基于paddle的ERINE模型进行迁移学习训练

教程:https://aistudio.baidu.com/aistudio/projectdetail/1735533?forkThirdPart=1

3、分步实现

3.1 获取数据

(1)数据解压

!unzip  /home/aistudio/data/data8164/THUCNews.zip  

解压之后结果有14个类别,文件夹里边是新闻文本:

(2)将文本转成变量,这里为了好计算,我只选了新闻标题做文本分类

(后续觉得数据量太小,就选了全量数据来做,遇到了正则表达式处理慢和分词慢的情况,推荐FlashText,中英文符号处理)

def read_data(file_dir):    z_list = []for parent, dirnames, filenames in os.walk(file_dir):if parent==file_dir:print('父目录')else:for curDir, dirs, files in os.walk(parent):print("当前文件夹:",curDir)label = curDir.split('/')[-1]title_list = []for i in range(len(files)):fo = open(parent+'/'+files[i],'r',encoding='utf-8')title = fo.readline().strip()title_list.append((title,label))z_list.extend(title_list)return z_list
file_dir = '/home/aistudio/THUCNews'
data = read_data(file_dir)

3.2 中文分词

  • 基于jieba的分词

jieba_c_list = []
#停用词字典
stopwords = {}.fromkeys([ line.rstrip() for line in open('/home/aistudio/stopwords.txt') ])
for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)s_list = jieba.cut(s_t,cut_all=False)final = []for seg in s_list:#    seg = seg.encode('gbk')#    print(seg)if seg not in stopwords:# print(seg)final.append(seg)jieba_c_list.append((final,data[i][1]))
  • 基于paddlehub的lac分词

lac = paddlehub.Module(name='lac')
lac_list = []
for i in range(len(data)):s_t = data[i][0].replace(' ','')s_t = re.sub(r'\d','',s_t)s_t = re.sub(r'\((.*?)\)', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\,', '', s_t)s_t = re.sub(r'\.', '', s_t)s_t = re.sub(r'\%', '', s_t)s_t = re.sub(r'\:', '', s_t)s_t = re.sub(r'\《', '', s_t)s_t = re.sub(r'\》', '', s_t)results = lac.lexical_analysis(texts=[s_t],batch_size=1)lac_ = results[0]['word']# print(lac_)lac_list.append((lac_,data[i][1]))

3.3 创建语料字典,并将语料序列化

  • 基于语料本身的序列化

(1)构建语料字典

# 构造词典,统计每个词的频率,并根据频率将每个词转换为一个整数id
def build_dict(corpus):word_freq_dict = dict()for sentence,_ in corpus:for word in sentence:if word not in word_freq_dict:word_freq_dict[word] = 0word_freq_dict[word] += 1word_freq_dict = sorted(word_freq_dict.items(), key = lambda x:x[1], reverse = True)word2id_dict = dict()word2id_freq = dict()# 一般来说,我们把oov和pad放在词典前面,给他们一个比较小的id,这样比较方便记忆,并且易于后续扩展词表# word2id_dict['[oov]'] = 0# word2id_freq[0] = 1e10# word2id_dict['[pad]'] = 1# word2id_freq[1] = 1e10for word, freq in word_freq_dict:word2id_dict[word] = len(word2id_dict)word2id_freq[word2id_dict[word]] = freqreturn word2id_freq, word2id_dictword2id_freq, word2id_dict = build_dict(jieba_c_list)
vocab_size = len(word2id_freq)
print("there are totoally %d different words in the corpus" % vocab_size)
for _, (word, word_id) in zip(range(10), word2id_dict.items()):print("word %s, its id %d, its word freq %d" % (word, word_id, word2id_freq[word_id]))

(2)ID序列化

label_dict ={'时政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娱乐':6,'房产':7,'社会':8,'财经':9,'游戏':10,'体育':11,'时尚':12,'家居':13,'教育':14}
def convert_corpus_to_id(corpus, word2id_dict):data_set = []for sentence, sentence_label in corpus:# 将句子中的词逐个替换成id,如果句子中的词不在词表内,则替换成oov# 这里需要注意,一般来说我们可能需要查看一下test-set中,句子oov的比例,# 如果存在过多oov的情况,那就说明我们的训练数据不足或者切分存在巨大偏差,需要调整sentence = [word2id_dict[word] if word in word2id_dict \else word2id_dict['[oov]'] for word in sentence]    data_set.append((sentence, label_dict[sentence_label]))return data_settrain_corpus = convert_corpus_to_id(jieba_c_list, word2id_dict)
print("%d tokens in the corpus" % len(train_corpus))
print(train_corpus[:5])
  • 基于word2vec或已有字典的序列化

from paddlehub.reader.tokenization import load_vocablabel_dict ={'时政':1,'星座':2,'股票':3,'彩票':4,'科技':5,'娱乐':6,'房产':7,'社会':8,'财经':9,'游戏':10,'体育':11,'时尚':12,'家居':13,'教育':14}# 这是把 中文词语 转化为 词表 中对应 ID 的函数
def convert_tokens_to_ids(vocab, tokens): # 输入为词表,和要转化的 textwids = [] # 初始化一个空的集合,用于存放输出#tokens = text.split(" ") # 将传入的 text 用 空格 做分割,变成 词语字符串 的列表for token in tokens: # 每次从列表里取出一个 词语wid = vocab.get(token, None)if not wid:wid = vocab["unknown"]wids.append(wid)return widsmodule = paddlehub.Module(name="word2vec_skipgram") # 实例化 word2vec_skipgram 模型vocab = load_vocab(module.get_vocab_path()) # 获得 词表 字典# 我们要获取词表,直接利用 paddlehub.reader.tokenization 中的 load_vocab 函数即可
# load_vocab 函数的输入是具体的词表文件,这里我们用 word2vec_skipgram 附带的词表
# 模块的实例化对象 module 下,用 get_vocab_path() 方法
# 该方法可以在指定的 Module 中(这里就是word2vec_skipgram)查找 assets 文件夹下面有没有 vocab.txt 文件
# 如果找到,则返回该文件的 具体文件路径
# load_vocab 函数的返回值是一个 字典,里面 key 为 词语,value 是词语对应的 IDtokens_ids = []
for item,_ in lac_list:item_ids = convert_tokens_to_ids(vocab, item) # 获得组成句子的 词语 的 ID 列表tokens_ids.append((item_ids,label_dict[_]))for i in range(5):print("token: %s; id: %s" % (lac_list[i], tokens_ids[i]))

3.4 词嵌入

  • 基于embedding的词嵌入

embedding在构建神经网络模型时使用。函数为paddle.nn.Embedding

Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))
  • 基于word2vec的词嵌入

Word2Vec有GBOW和skip-gram两种实现方式,实现的库有gensim、paddlehub,这里用gensim实现。

import gensim #将取字符串,忽略类别(已转成ID序列)
from gensim.models.word2vec import Word2Vecnum_features = 100     # Word vector dimensionality
num_workers = 8       # Number of threads to run in paralleltrain_texts = list(map(lambda x: list(x.split()), train_texts))
model = Word2Vec(train_texts, workers=num_workers, size=num_features)
model.init_sims(replace=True)# save model
model.save("./word2vec.bin")
#也可转换为txt

结果的第一行是(词数量,词向量的维度就是num_features)

第二行就是词向量ID和对应的词向量

3.5 构建分类模型

基于LSTM的分类模型(此处的class_num改为和新闻类别一致的数量):

from paddle.nn import LSTM, Embedding, Dropout, Linear
import paddle.nn.functional as F# 定义一个用于情感分类的网络实例,SentimentClassifier
class SentimentClassifier(paddle.nn.Layer):def __init__(self, hidden_size, vocab_size, class_num=14, num_steps=128, num_layers=1, init_scale=0.1, dropout=None):# 参数含义如下:# 1.hidden_size,表示embedding-size,hidden和cell向量的维度# 2.vocab_size,模型可以考虑的词表大小# 3.class_num,情感类型个数,可以是2分类,也可以是多分类# 4.num_steps,表示这个情感分析模型最大可以考虑的句子长度# 5.num_layers,表示网络的层数# 6.init_scale,表示网络内部的参数的初始化范围# 长短时记忆网络内部用了很多Tanh,Sigmoid等激活函数,这些函数对数值精度非常敏感,# 因此我们一般只使用比较小的初始化范围,以保证效果super(SentimentClassifier, self).__init__()self.hidden_size = hidden_sizeself.vocab_size = vocab_sizeself.class_num = class_numself.init_scale = init_scaleself.num_layers = num_layersself.num_steps = num_stepsself.dropout = dropout# 声明一个LSTM模型,用来把每个句子抽象成向量self.simple_lstm_rnn = LSTM(input_size=hidden_size, hidden_size=hidden_size, num_layers=num_layers)# 声明一个embedding层,用来把句子中的每个词转换为向量self.embedding = Embedding(num_embeddings=vocab_size, embedding_dim=hidden_size, sparse=False, weight_attr=paddle.ParamAttr(initializer=paddle.nn.initializer.Uniform(low=-init_scale, high=init_scale)))# 在得到一个句子的向量表示后,需要根据这个向量表示对这个句子进行分类# 一般来说,可以把这个句子的向量表示乘以一个大小为[self.hidden_size, self.class_num]的W参数,# 并加上一个大小为[self.class_num]的b参数,从而达到把句子向量映射到分类结果的目的# 我们需要声明最终在使用句子向量映射到具体情感类别过程中所需要使用的参数# 这个参数的大小一般是[self.hidden_size, self.class_num]self.cls_fc = Linear(in_features=self.hidden_size, out_features=self.class_num, weight_attr=None, bias_attr=None)self.dropout_layer = Dropout(p=self.dropout, mode='upscale_in_train')def forward(self, input, label):# 首先我们需要定义LSTM的初始hidden和cell,这里我们使用0来初始化这个序列的记忆init_hidden_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')init_cell_data = np.zeros((self.num_layers, batch_size, embedding_size), dtype='float32')# 将这些初始记忆转换为飞桨可计算的向量# 设置stop_gradient=True,避免这些向量被更新,从而影响训练效果init_hidden = paddle.to_tensor(init_hidden_data)init_hidden.stop_gradient = Trueinit_cell = paddle.to_tensor(init_cell_data)init_cell.stop_gradient = Trueinit_h = paddle.reshape(init_hidden, shape=[self.num_layers, -1, self.hidden_size])init_c = paddle.reshape(init_cell, shape=[self.num_layers, -1, self.hidden_size])# 将输入的句子的mini-batch转换为词向量表示x_emb = self.embedding(input)x_emb = paddle.reshape(x_emb, shape=[-1, self.num_steps, self.hidden_size])if self.dropout is not None and self.dropout > 0.0:x_emb = self.dropout_layer(x_emb)# 使用LSTM网络,把每个句子转换为向量表示rnn_out, (last_hidden, last_cell) = self.simple_lstm_rnn(x_emb, (init_h, init_c))last_hidden = paddle.reshape(last_hidden[-1], shape=[-1, self.hidden_size])# 将每个句子的向量表示映射到具体的情感类别上projection = self.cls_fc(last_hidden)pred = F.softmax(projection, axis=-1)# 根据给定的标签信息,计算整个网络的损失函数,这里我们可以直接使用分类任务中常使用的交叉熵来训练网络loss = F.softmax_with_cross_entropy(logits=projection, label=label, soft_label=False)loss = paddle.mean(loss)# 最终返回预测结果pred,和网络的lossreturn pred, loss

(5)训练和预测

# 编写一个迭代器,每次调用这个迭代器都会返回一个新的batch,用于训练或者预测
def build_batch(word2id_dict, corpus, batch_size, epoch_num, max_seq_len, shuffle = True, drop_last = True):# 模型将会接受的两个输入:# 1. 一个形状为[batch_size, max_seq_len]的张量,sentence_batch,代表了一个mini-batch的句子。# 2. 一个形状为[batch_size, 1]的张量,sentence_label_batch,每个元素都是非0即1,代表了每个句子的情感类别(正向或者负向)sentence_batch = []sentence_label_batch = []for _ in range(epoch_num): #每个epoch前都shuffle一下数据,有助于提高模型训练的效果#但是对于预测任务,不要做数据shuffleif shuffle:random.shuffle(corpus)for sentence, sentence_label in corpus:sentence_sample = sentence[:min(max_seq_len, len(sentence))]if len(sentence_sample) < max_seq_len:for _ in range(max_seq_len - len(sentence_sample)):sentence_sample.append(word2id_dict['[pad]'])sentence_sample = [[word_id] for word_id in sentence_sample]sentence_batch.append(sentence_sample)sentence_label_batch.append([sentence_label])if len(sentence_batch) == batch_size:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")sentence_batch = []sentence_label_batch = []if not drop_last and len(sentence_batch) > 0:yield np.array(sentence_batch).astype("int64"), np.array(sentence_label_batch).astype("int64")#训练预测
def train():step = 0sentiment_classifier = SentimentClassifier(embedding_size, vocab_size, num_steps=max_seq_len, num_layers=1)# 创建优化器Optimizer,用于更新这个网络的参数        optimizer = paddle.optimizer.Adam(learning_rate=0.01, beta1=0.9, beta2=0.999, parameters= sentiment_classifier.parameters()) sentiment_classifier.train()for sentences, labels in build_batch(word2id_dict, train_corpus, batch_size, epoch_num, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)pred, loss = sentiment_classifier(sentences_var, labels_var)# 后向传播loss.backward()# 最小化lossoptimizer.step()# 清除梯度optimizer.clear_grad()step += 1if step % 100 == 0:print("step %d, loss %.3f" % (step, loss.numpy()[0]))# 我们希望在网络训练结束以后评估一下训练好的网络的效果# 通过eval()函数,将网络设置为eval模式,在eval模式中,网络不会进行梯度更新eval(sentiment_classifier)def eval(sentiment_classifier):sentiment_classifier.eval()# 这里我们需要记录模型预测结果的准确率# 对于二分类任务来说,准确率的计算公式为:# (true_positive + true_negative) / # (true_positive + true_negative + false_positive + false_negative)tp = 0.tn = 0.fp = 0.fn = 0.for sentences, labels in build_batch(word2id_dict, test_corpus, batch_size, 1, max_seq_len):sentences_var = paddle.to_tensor(sentences)labels_var = paddle.to_tensor(labels)# 获取模型对当前batch的输出结果pred, loss = sentiment_classifier(sentences_var, labels_var)# 把输出结果转换为numpy array的数据结构# 遍历这个数据结构,比较预测结果和对应label之间的关系,并更新tp,tn,fp和fnpred = pred.numpy()for i in range(len(pred)):if labels[i][0] == 1:if pred[i][1] > pred[i][0]:tp += 1else:fn += 1else:if pred[i][1] > pred[i][0]:fp += 1else:tn += 1# 输出最终评估的模型效果print("the acc in the test set is %.3f" % ((tp + tn) / (tp + tn + fp + fn)))

(全量数据,带新闻内容的)预测准确率在0.682,考虑优化特征向量或者更改其他模型!

参考资料:

https://blog.csdn.net/qq_42067550/article/details/106101183

https://paddleinference.paddlepaddle.org.cn/product_introduction/inference_intro.html

https://blog.csdn.net/xiaoxiaojie521/article/details/97240436

NLP实战-中文新闻文本分类相关推荐

  1. [Pytorch系列-61]:循环神经网络 - 中文新闻文本分类详解-3-CNN网络训练与评估代码详解

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...

  2. 基于 LSTM-Attention 的中文新闻文本分类

    1.摘 要 经典的 LSTM 分类模型,一种是利用 LSTM 最后时刻的输出作为高一级的表示,而另一种是将所有时刻的LSTM 输出求平均作为高一级的表示.这两种表示都存在一定的缺陷,第一种缺失了前面的 ...

  3. [Pytorch系列-60]:循环神经网络 - 中文新闻文本分类详解-2-LSTM网络训练与评估代码详解

    作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客 本文网址:https://blog.csdn.net/HiWangWenBing/article/detai ...

  4. 零基础入门天池NLP赛事之——新闻文本分类(5)

    基于深度学习的文本分类 一.学习目标: 学习Word2Vec的使用和基础原理 学习使用TextCNN.TextRNN进行文本表示 学习使用HAN网络结构完成文本分类 二.文本表示方法 Part3: 词 ...

  5. NLP入门之新闻文本分类竞赛——文本分类模型

    一.Word2Vec word2vec模型背后的基本思想是对出现在上下文环境里的词进行预测.对于每一条输入文本,我们选取一个上下文窗口和一个中心词,并基于这个中心词去预测窗口里其他词出现的概率.因此, ...

  6. NLP实践(新闻文本分类)-数据读取与数据分析

    数据读取与数据分析 读取数据 数据分析 句子长度分析 新闻类别分类 字符分布统计 统计出现频率最多的字符 数据分析结论 总结 读取数据 赛题数据虽然是文本数据,每个新闻是不定长的,但任然使用csv格式 ...

  7. NLP实战之HAN文本分类

    HAN(层叠注意力)神经网络文本分类 原理讲解 HAN出处:论文Hierarchical Attention Networks for Document Classification 可以参见讲解文献 ...

  8. 基于GPT2实现中文新闻文本分类任务

    前言 大家好,我是阿光. 本专栏整理了<PyTorch深度学习项目实战100例>,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集. 正在更新 ...

  9. 【项目实战课】NLP入门第1课,人人免费可学,基于TextCNN的新闻文本分类实战...

    欢迎大家来到我们的项目实战课,本期内容是<基于TextCNN的新闻文本分类实战>. 所谓项目课,就是以简单的原理回顾+详细的项目实战的模式,针对具体的某一个主题,进行代码级的实战讲解,可以 ...

最新文章

  1. 如何提升科研能力?以下这点最重要!
  2. 近年来霸屏CNS封面的领域,值得关注
  3. 【万里征程——Windows App开发】DatePickerFlyout、TimePickerFlyout的使用
  4. Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedClient)的创建过程分析...
  5. TypeScript里的空值合并运算符(双问号)用法
  6. Exchange Server 2003邮件服务器系统的基本部署思路
  7. Firefox(火狐浏览器)彩蛋
  8. iOS开发UIScrollView的底层实现
  9. 5g的基础知识,发展及现状
  10. 又一主播逃税被罚!快手主播“驴嫂平荣”被追缴并罚款6200.3万元
  11. 全球顶级金融机构Citadel:堡垒如何建成|精品投行系列二
  12. Apollo添加新的can通信接口的GPS设备
  13. c语言记账系统源程序,C语言会计记账管理系统.doc
  14. 苹果CMS自动定时采集教程
  15. 【3D目标检测】Rethinking Pseudo-LiDAR Representation
  16. 苹果4s怎么越狱_iPhone 12系列细节曝光:苹果调整屏幕尺寸
  17. 桌面计算机打开无响应,电脑桌面假死(点击无反应)怎么解决?
  18. Win10如何用命令打开控制面板
  19. 计算物体自由下落距离
  20. 硬件设计人员制作电路板需提供的材料

热门文章

  1. sublime text 2快捷键总结
  2. 案例 自动办公_1300张办公系列前台参考图,请您查收!
  3. 驱动级的自动按键_Aqara全自动智能推拉锁D100,体验全自动开门的便捷
  4. tornado学习笔记day06-应用安全
  5. 关于安装deepin+window10双系统有时没有声音的问题
  6. 【零散积累】传输文件(sz/rz/scp命令)
  7. Django 部署基础【使用 Nginx + uWSGI 的方式来部署来 Django】
  8. 【Java】HashMap源码(1.7)
  9. micropython web ws2812_MicroPython实例之TPYBoard v102炫彩跑马灯WS2812B
  10. 小程序 ajax 加载,小程序实战-小程序网络请求异步加载