目录

1.案例简介

2 代码

2.1 load_data.py

2.2 load_data_iter.py

2.3 FastText.py

2.4 train.py

2.5 predict.py

2.6 run.py

2.7 实验结果(部分

3 代码地址


1.案例简介

数据集从 THUCNews 上抽取 20 万条新闻标题,文本长度在 20~30 字,总计 10 个类别。每类 2 万条进行分类操作,并基于 PyTorch 完成 FastText 模型处理。

FastText模型是脸书开源的一个词向量与文本分类工具。其在2016年开源,典型应用场景是「带监督的文本分类问题」。其可以提供简单而高效的文本分类和表征学习的方法,性能比肩深度学习而且速度更快。

FastText模型结合了自然语言处理和机器学习中最成功的理念。我们另外采用了一个softmax层级(利用了类别不均衡分布的优势)来加速运算过程。

FastText模型是一个快速文本分类模型算法,与基于神经网络的分类模型算法相比有以下优点:

1)FastText模型在保持高精度的情况下提高了训练速度和测试速度;
2)FastText模型不需要预训练好的词向量,其可以自己训练词向量,
3)FastText模型两个重要的优化是Hierarchical softmax和N-gram。

FastText模型网络分为三层:

输入层:对文档插入之后的向量,包含有N-gram特征。
隐含层:对输入数据的求和平均。
输出层:文档对应标签。

2 代码

2.1 load_data.py

将词转换为编号

比如:一开始文档中的词是长这个样子

内容  标签

对文件中的中文进行分词处理,按照一个字一个字的分开:

['中','华','女','子','学','院',':','本','科','层','次','仅','1','专','业','招','男','生']

分开之后对文件中所有的词进行频率统计,按照频率高低进行排列:

比如:

{'中': 17860, '华': 5053, '女': 8568, '子': 10246, '学': 11069, '院': 2833, ':': 28269, '本': 6649, '科': 4375, '层': 776, '次': 3159, '仅': 1793, '1': 40420, '专': 4134, '业': 9748, '招': 4648, '男': 5884, '生': 15370, '两': 4880, '天': 6662, '价': 11663, '网': 8573, '站': 1760, '背': 662, '后': 7306, '重': 5960, '迷': 1248, '雾': 81, '做': 1827, '个': 4075, '究': 1372, '竟': 541, '要': 4044, '多': 5170, '少': 2108, '钱': 1551, '东': 4555, '5': 18163, '环': 1898, '海': 6229, '棠': 65, '公': 10769, '社': 1165, '2': 31856, '3': 20291, '0': 60319, '-': 7944, '9': 15626, '平': 8207, '居': 5716, '准': 1859, '现': 7473, '房': 8181, '8': 13315, '折': 2990, '优': 1978, '惠': 1560, '卡': 3009, '佩': 467, '罗': 2536, '告': 2843, '诉': 1055, '你': 1456, '德': 3209, '国': 24079, '脚': 491, '猛': 379, '的': 8753, '原': 1782, '因': 2959, ' ': 80926, '不': 12798, '希': 1264, '望': 2504, '英': 5067, '战': 6391, '踢': 268, '点': 5623, '球': 5074, '岁': 2528, '老': 3982, '太': 1699, '为': 7095, '饭': 313, '扫': 299, '地': 7875, '4': 13832, '年': 18565, '获': 3876, '授': 381, '港': 3341, '大': 26024, '荣': 538, '誉': 265, '士': 2674, '记': 1858, '者': 3507, '回': 4644, '访': 1410, '震': 2040, '可': 5042, '乐': 2875, '孩': 1379, '将': 10546, '受': 3724, '邀': 440, '赴': 782, '美': 11941, '参': 1526, '观': 1635, '冯': 252, '伦': 831, '徐': 376, '若': 394, '�': 763, ..........}

排序之后再对词重新编号最后面两个添加<UNK>:字总数,<PAD>:字总数+1

{'中': 1, '华': 2, '女': 3, '子': 4,.......,<UNK>:字总数,<PAD>:字总数+1}

保存好这个词典

在建立数据集的时候,对每一行的数据都要进行填充或者删除就是用词典来进行的。

import os
import pickle as pkl
from tqdm import tqdm
MAX_VOCAB_SIZE = 10000 #词表长度限制
UNK,PAD = '<UNK>','<PAD>' #未知字,padding符号# 编辑词典函数
def build_vocab(file_path,tokenizer,max_size,min_freq):vocab_dic = {}# 打开路径文件with open(file_path,'r',encoding='UTF-8') as f:for line in tqdm(f):lin = line.strip()# 去掉其中的空行if not lin:continue# 去除后面的数字content = lin.split('\t')[0]# 对单词进行分词操作(字符级别)for word in tokenizer(content):# 构建词典  统计每个词出现的频率vocab_dic[word] = vocab_dic.get(word, 0)+1# 将出现频率高的词排在前面vocab_list = sorted([_ for _ in vocab_dic.items() if _[1] >= min_freq], key=lambda x: x[1], reverse=True)[:max_size]# 将所有的词重新按照频率高到低顺序  依次编号vocab_dic = {word_count[0]: idx for idx, word_count in enumerate(vocab_list)}# 向 vocab_dic 中添加两个特殊单词的映射:UNK 表示未知单词,PAD 表示填充单词。UNK 的编号为词汇表大小,而 PAD 的编号为词汇表大小加 1。vocab_dic.update({UNK: len(vocab_dic), PAD: len(vocab_dic)+1})return vocab_dic# 编辑建立数据集函数
def build_dataset(config,ues_word):# 根据 ues_word 变量的值在单词级别和字符级别之间切换分词方式if ues_word:tokenizer = lambda x: x.split('') # 以空格隔开,word-level  单词级别else:tokenizer = lambda x: [y for y in x] # char-level  字符级别# 如果存在 词典文件则直接读取if os.path.exists(config.vocab_path):vocab = pkl.load(open(config.vocab_path,'rb'))# 不存在则创建词典文件else:# config.train_path = './data/train.txt'vocab = build_vocab(config.train_path, tokenizer=tokenizer,max_size=MAX_VOCAB_SIZE, min_freq=1)pkl.dump(vocab, open(config.vocab_path, 'wb'))print(f"Vocab size:{len(vocab)}")def load_dataset(path, pad_size=32):contents = []# 读取路径with open(path, 'r', encoding='UTF-8') as f:for line in tqdm(f):lin = line.strip()if not lin:continue# 存储内容和标签content, label = lin.split('\t')words_line = []# 以字符方式进行分词处理token = tokenizer(content)# 记录所有文件中词的数量seq_len = len(token)# token = ['传', '凡', '客', '诚', '品', '裁', '员', '5', '%', ' ', '电', '商', '寒', '冬', '或', '提', '前', '到', '来']# 将token固定为同样长度if pad_size:if len(token) < pad_size:token.extend([PAD]*(pad_size - len(token)))else:token = token[:pad_size]seq_len = pad_size# 讲统一填充的词完成词到编号的转换for word in token:words_line.append(vocab.get(word, vocab.get(UNK)))contents.append((words_line, int(label), seq_len))return contents# 加载训练集train = load_dataset(config.train_path, config.pad_size)dev = load_dataset(config.dev_path, config.pad_size)test = load_dataset(config.test_path, config.pad_size)# 返回训练集、验证集和测试集return vocab, train, dev, test

2.2 load_data_iter.py

将数据按照批量进行。

批量记载数据的原因:深度学习模型的参数非常多,为了得到模型的参数,需要用大量的数据对模型进行训练,所以数据量一般是相当大的,不可能一次性加载到内存中对所有数据进行向前传播和反向传播,因此需要分批次将数据加载到内存中对模型进行训练。使用数据加载器的目的就是方便分批次将数据加载到模型,以分批次的方式对模型进行迭代训练。

import torchclass DatasetIterater(object):def __init__(self, batches, batch_size, device):# 批次大小(在config中定义)self.batch_size = batch_size# 数据self.batches = batches# //整除操作符  // 操作符会向下取整,舍弃余数。self.n_batches = len(batches) // batch_sizeself.residue = False # 记录batch数量是否为整数if len(batches) % self.n_batches != 0:self.residue = Trueself.index = 0self.device = devicedef _to_tensor(self, datas):# 讲数据集转为tensorx = torch.LongTensor([_[0] for _ in datas]).to(self.device)y = torch.LongTensor([_[1] for _ in datas]).to(self.device)# pad 前的长度(超过pad_size的设为pad_size)seq_len = torch.LongTensor([_[2] for _ in datas]).to(self.device)return (x, seq_len), ydef __next__(self):# 有剩余数据并且当前索引小于批次大小if self.residue and self.index < self.n_batches:batches = self.batches[self.index * self.batch_size: len(self.batches)]self.index += 1batches = self._to_tensor(batches)return batcheselif self.index >= self.n_batches:self.index = 0raise StopIterationelse:batches = self.batches[self.index * self.batch_size: (self.index + 1) * self.batch_size]self.index += 1batches = self._to_tensor(batches)return batchesdef __iter__(self):return selfdef __len__(self):if self.residue:return self.n_batches + 1else:return self.n_batchesdef build_iterator(dataset, config, predict):if predict is True:config.batch_size = 1iter = DatasetIterater(dataset, config.batch_size, config.device)return iter

2.3 FastText.py

代码里面有所有需要模型的配置参数,以及模型类

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np# 编写参数配置类
class Config(object):# 配置参数def __init__(self):self.model_name = 'FastText'self.train_path = './data/train.txt'# 训练集self.dev_path = './data/dev.txt'# 验证集self.test_path = './data/test.txt'# 测试集self.predict_path = './data/predict.txt'self.class_list = [x.strip() for x in open('./data/class.txt', encoding='utf-8').readlines()]self.vocab_path = './data/vocab.pkl'  # 词表# 模型训练结果self.save_path = './saved dict/' + self.model_name + '.ckpt'self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')self.dropout = 0.5 #随机失活#若超过1000 patch效果还没提升,则提前结束训练self.require_improvement = 1000self.num_classes = len(self.class_list)#类别数self.n_vocab = 0 #词表大小,在运行时赋值self.num_epochs = 5 #epoch数self.batch_size = 32 #mini-batch大小self.pad_size = 32 #每句话处理成的长度(短填长切)self.learning_rate = 1e-3 #学习率self.embed = 300 #字向量维度self.hidden_size =256 #隐藏层大小self.filter_sizes = (2, 3, 4)  # 卷积核尺寸self.num_filters = 256  # 卷积核数量(channels数)# 编写模型类
class Model(nn.Module):def __init__(self,config):super(Model,self).__init__()self.embedding = nn.Embedding(config.n_vocab,  # 词汇表达大小config.embed,    # 词向量维度padding_idx=config.n_vocab-1 # 填充)self.dropout = nn.Dropout(config.dropout)   # 丢弃self.fc1 = nn.Linear(config.embed, config.hidden_size)   # 全连接层self.dropout = nn.Dropout(config.dropout)  # 丢弃self.fc2 = nn.Linear(config.hidden_size, config.num_classes)   #全连接层# 前向传播计算def forward(self, x):# 词嵌入out_word = self.embedding(x[0])out = out_word.mean(dim=1)out = self.dropout(out)# print(out.shape)out = self.fc1(out)out = F.relu(out)out = self.fc2(out)return out

2.4 train.py

训练数据集,

import numpy as np
import torch
import torch.nn.functional as F
from sklearn import metrics
#编写训练函数
# 传入的是 测试集和验证集
def train(config,model,train_iter,dev_iter):print("begin")model.train()# 优化器optimizer = torch.optim.Adam(model.parameters(), lr=config.learning_rate)total_batch = 0 # 记录进行到多少batchdev_best_loss = float('inf')last_improve = 0 # 记录上次验证集loss下降的batch数flag = False # 记录是否很久没有效果提升for epoch in range(config.num_epochs):print('Epoch[{}/{}]'.format(epoch+1,config.num_epochs))# 批量训练for i ,(trains ,labels) in enumerate(train_iter):# 将训练集放在模型中outputs = model(trains)# 清空模型梯度信息  在每次迭代时,需要将参数的梯度清空,以免当前的梯度信息影响到下一次迭代model.zero_grad()# 计算损失函数loss = F.cross_entropy(outputs, labels)# 反向传播loss.backward()# 根据上面计算得到的梯度信息和学习率  对模型的参数进行优化optimizer.step()if total_batch % 100 == 0:# 每多少轮输出在训练集和验证集上的效果true = labels.data.cpu()predict = torch.max(outputs.data, 1)[1].cpu()train_acc = metrics.accuracy_score(true, predict)dev_acc, dev_loss = evaluate(config, model, dev_iter)if dev_loss < dev_best_loss:dev_best_loss = dev_loss# 存储模型torch.save(model.state_dict(), config.save_path)# 记录batch数last_improve = total_batch# {2:6.2%} 是一个格式化字符串语法,表示将第三个参数格式化为一个百分数,并使用右对齐方式,并在左侧填充空格,总宽度为 6 个字符,保留两位小数。msg = 'Iter: {0:>6}, Train Loss: {1:>5.2}, Train Acc: {2:6.2%} ,''Val Loss :{3:>5.2}, Val Acc: {4:>6.2%}'print(msg.format(total_batch,loss.item(),train_acc,dev_loss,dev_acc))model.train()total_batch +=1if total_batch - last_improve > config.require_improvement:# 验证集1oss超过1000 batch没下降,结束训练print("No optimization for a long time,auto-stopping...")flag = Truebreakif flag:break# 编写评价函数
def evaluate(config,model,data_iter,test=False):# 将模型切换到评估模式 在评估模式下,模型的行为与训练模式下略有不同。具体来说,评估模式下模型会关闭一些对训练过程的辅助功能,例如 dropout 和 batch normalization 等,并且不会对模型的参数进行更新。model.eval()loss_total = 0predict_all = np.array([], dtype=int)labels_all = np.array([], dtype=int)# 防止模型参数更新with torch.no_grad():for texts, labels in data_iter:outputs = model(texts)# 损失函数loss = F.cross_entropy(outputs, labels)# 损失值累加loss_total += losslabels = labels.data.cpu().numpy()predict = torch.max(outputs.data, 1)[1].cpu().numpy()# labels_all 是所有样本的真实标签labels_all = np.append(labels_all, labels)# predict_all 是所有样本的预测标签predict_all = np.append(predict_all, predict)acc = metrics.accuracy_score(labels_all, predict_all)if test:# config.class_list 是所有可能的类别列表report = metrics.classification_report(labels_all, predict_all, target_names=config.class_list, digits=4)# 混淆矩阵confusion = metrics.confusion_matrix(labels_all, predict_all)return acc, loss_total / len(data_iter), report, confusionreturn acc, loss_total / len(data_iter)

2.5 predict.py

import torch
import numpy as np
from train import evaluate
MAX_VOCAB_SIZE = 10000
UNK,PAD = '<UNK>','<PAD>'
tokenizer = lambda x:[y for y in x]     #char-level# 编写测试函数
def test(config,model,test_iter):# test# 加载训练好的模型model.load_state_dict(torch.load(config.save_path))model.eval()#开启评价模式test_acc,test_loss,test_report,test_confusion = evaluate(config,model,test_iter,test=True)msg = 'Test Loss:{0:>5.2},Test Acc:{1:>6.28}'print(msg.format(test_loss,test_acc))print("Precision,Recall and Fl-Score...")print(test_report)print("Confusion Matrix...")print(test_confusion)# 编写加载数据函数
def load_dataset(text, vocab, config, pad_size=32):contents = []for line in text:lin = line.strip()if not lin:continuewords_line = []token = tokenizer(line)seq_len = len(token)if pad_size:if len(token) < pad_size:token.extend([PAD](pad_size - len(token)))else:token = token[:pad_size]seq_len = pad_size# 单词到编号的转换for word in token:words_line.append(vocab.get(word, vocab.get(UNK)))contents.append((words_line, int(0), seq_len))return contents  # 数据格式为[([..],O),([.·],1),.]# 编写标签匹配函数
def match_label(pred,config):label_list = config.class_listreturn label_list[pred]# 编写预测函数
def final_predict(config, model, data_iter):map_location = lambda storage, loc: storagemodel.load_state_dict(torch.load(config.save_path, map_location=map_location))model.eval()predict_all = np.array([])with torch.no_grad():for texts, _ in data_iter:outputs = model(texts)pred = torch.max(outputs.data, 1)[1].cpu().numpy()pred_label = [match_label(i, config)for i in pred]predict_all = np.append(predict_all, pred_label)return predict_all

2.6 run.py

from FastText import Config
from FastText import Model
from load_data import build_dataset
from load_data_iter import build_iterator
from train import train
from predict import test,load_dataset,final_predict# 测试文本
text = ['国考网上报名序号查询后务必牢记。报名参加2011年国家公务员考试的考生:如果您已通过资格审查,那么请于10月28日8:00后,登录考录专题网站查询自己的报名序号']
if __name__ == "__main__":config = Config()print("Loading data...")vocab, train_data, dev_data, test_data = build_dataset(config, False)#1,批量加载测试数据# 批量记载数据的原因:深度学习模型的参数非常多,为了得到模型的参数,需要用大量的数据对模型进行训练,所以数据量一般是相当大的,# 不可能一次性加载到内存中对所有数据进行向前传播和反向传播,因此需要分批次将数据加载到内存中对模型进行训练。使用数据加载器的# 目的就是方便分批次将数据加载到模型,以分批次的方式对模型进行迭代训练。train_iter = build_iterator(train_data, config, False)dev_iter = build_iterator(dev_data, config, False)test_iter = build_iterator(test_data, config, False)config.n_vocab = len(vocab)#2,加载模型结构model = Model(config).to(config.device)train(config, model, train_iter, dev_iter)#3.测试test(config, model, test_iter)print("+++++++++++++++++")#4.预测content = load_dataset(text, vocab, config)predict_iter = build_iterator(content, config, predict=True)result = final_predict(config, model, predict_iter)for i, j in enumerate(result):print('text:{}'.format(text[i]), '\t', 'label:{}'.format(j))

2.7 实验结果(部分

3 代码地址

代码来源:《自然语言处理应用与实战》 韩少云等编著 著

代码地址:c4d2/nlp_demo: nlp相关案例 (github.com)

【nlp自然语言处理实战】案例---FastText模型文本分类相关推荐

  1. NLP 自然语言处理实战

    前言 自然语言处理 ( Natural Language Processing, NLP) 是计算机科学领域与人工智能领域中的一个重要方向.它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和 ...

  2. NLP自然语言处理系列——LDA主题词模型探析

    NLP自然语言处理系列--LDA主题词模型探析 时间 2015-06-17 22:39:57  十一城elevencitys.com 原文  http://elevencitys.com/2015/0 ...

  3. 【NLP】基于python fasttext的文本分类

    背景 文本分类中的深度学习算法比较多,各种算法也由于其复杂度适应不同的场景.这次介绍的fasttext也是一个结构比较简单模型.结构虽然简单,但效果不错,还快.并且除了python有相关实现的包外,在 ...

  4. 【NLP】主题模型文本分类

    自然语言处理之主题模型文本分类 LDA主题模型 1.主题模型(Topic Model) 主题模型是以非监督学习的方式对文集的隐含语义结构进行聚类的统计模型.主题模型主要被用于自然语言处理中的语义分析和 ...

  5. 面向可解释的NLP:北大、哈工大等提出文本分类的生成性解释框架

    作者 | Hui Liu, Qingyu Yin, William Yang Wang 译者 | Rachel 编辑 | Jane 出品 | AI科技大本营(ID: rgznai100) [导语]北大 ...

  6. fastText实现文本分类

    fastText实现文本分类 1. fastText的介绍  [用来获取词向量,进行文本分类的工具:分类的效率,得到词向量的效率高] 文档地址:https://fasttext.cc/docs/en/ ...

  7. 使用fastText实现文本分类-java版

    使用FastText实现文本分类-java版 文本分类又称自动文本分类,是指计算机将载有信心的一篇文本映射到预先给定的某一类别或某几个类别主题的过程,实现这一过程的算法模型叫做分类器.哈哈哈,这一句是 ...

  8. R语言构建xgboost文本分类模型(bag of words):xgb.cv函数交叉验证确定xgboost模型的最优子树个数、交叉验证获取最优子树之后构建最优xgboost模型并评估模型文本分类效能

    R语言构建xgboost文本分类模型(bag of words):xgb.cv函数交叉验证确定xgboost模型的最优子树个数.交叉验证获取最优子树之后构建最优xgboost模型并评估模型文本分类效能 ...

  9. 自然语言处理(NLP):06 word2vec训练中文模型-文本分类

    本章节主要研究内容:基于word2vec 提取特征 + 文本分类 finetune 就是用别人训练好的模型,加上我们自己的数据,来训练新的模型.finetune相当于使用别人的模型的前几层,来提取浅层 ...

最新文章

  1. 「文末预告,注意查收」告别枯燥的理论,回归技术
  2. matchers依赖_Hamcrest Matchers教程
  3. java flash截图_求大神们帮助, 如何在java中实现文字数据转换成图片或flash显示
  4. 安装ie浏览器的js脚本调试工具
  5. 机器学习系列补充:数据集准备和更正YSX包
  6. iis如何处理并发请求
  7. 除了 Coding,程序员获得收入的四大途径!
  8. anaconda 清华镜像及对应下载
  9. (重读)JavaScript高级程序设计第四版
  10. 企业IP地址怎么划分
  11. 无人驾驶常用专有名词
  12. 使用最小二乘法拟合曲线
  13. ppt圆形箭头怎么画
  14. 如何制作千千静听个性皮肤
  15. 【Excel】根据空格拆分单元格
  16. 提取Excel中的超链接
  17. matlab 轴系校中,轴系校中简介.ppt
  18. Linux系统的基本介绍
  19. 【从零开始学习Oracle数据库】(3)函数与子查询和连接查询
  20. 软件项目简明代码评审流程

热门文章

  1. c语言16进制转换为ascii码,C--16进制文本转换为ascii码
  2. python中os.fork()理解
  3. JAVA命名规范(阿里巴巴)及其口语化总结
  4. 【Elasticsearch-1】ES原理及安装es8.2.0
  5. Flutter常用插件和对.yaml讲解
  6. 微信小程序解决标签内文字因长度换行问题
  7. word2vec原理总结
  8. 软件建模与分析——G001-185-03
  9. win10path变量编辑变成长条框框
  10. 程序员匠性的唤醒和维护