深度学习每周学习总结N4:中文文本分类-Pytorch实现(基本分类(熟悉流程)、textCNN分类(通用模型)、Bert分类(模型进阶))

目录

    • [0. 总结:](#0. 总结:)
    • [1. 基础模型](#1. 基础模型)
      • [a. 数据加载](#a. 数据加载)
      • [b. 数据预处理](#b. 数据预处理)
      • [c. 模型搭建与初始化](#c. 模型搭建与初始化)
      • [d. 训练函数](#d. 训练函数)
      • [e. 评估函数](#e. 评估函数)
      • f.拆分数据集运行模型
      • [g. 结果可视化](#g. 结果可视化)
      • [h. 测试指定数据](#h. 测试指定数据)
    • [2. TextCNN(通用模型-待拓展)](#2. TextCNN(通用模型-待拓展))
    • [3. Bert(高级模型-待拓展)](#3. Bert(高级模型-待拓展))

0. 总结:

之前有学习过文本预处理的环节,对文本处理的主要方式有以下三种:

1:词袋模型(one-hot编码)

2:TF-IDF

3:Word2Vec(词向量(Word Embedding) 以及Word2vec(Word Embedding 的方法之一))

详细介绍及中英文分词详见pytorch文本分类(一):文本预处理

上上期主要介绍Embedding,及EmbeddingBag 使用示例(对词索引向量转化为词嵌入向量) ,上期主要介绍:应用三种模型的英文分类

本期将主要介绍中文基本分类(熟悉流程)、拓展:textCNN分类(通用模型)、拓展:Bert分类(模型进阶)

1. 基础模型

a. 数据加载

python 复制代码
import torch
import torch.nn as nn
import torchvision
from torchvision import transforms,datasets

import numpy as np
import pandas as pd

import os,PIL,pathlib,warnings
import matplotlib.pyplot as plt
import warnings

warnings.filterwarnings("ignore") # 忽略警告信息
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False   # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100  # 分辨率
python 复制代码
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device
复制代码
device(type='cuda')
python 复制代码
# 加载自定义中文数据集
train_data = pd.read_csv('./data/N4-train.csv',sep = '\t',header = None)
train_data.head()

| | 0 | 1 |
| 0 | 还有双鸭山到淮阴的汽车票吗13号的 | Travel-Query |
| 1 | 从这里怎么回家 | Travel-Query |
| 2 | 随便播放一首专辑阁楼里的佛里的歌 | Music-Play |
| 3 | 给看一下墓王之王嘛 | FilmTele-Play |

4 我想看挑战两把s686打突变团竞的游戏视频 Video-Play
python 复制代码
train_data[0]
复制代码
0                  还有双鸭山到淮阴的汽车票吗13号的
1                            从这里怎么回家
2                   随便播放一首专辑阁楼里的佛里的歌
3                          给看一下墓王之王嘛
4              我想看挑战两把s686打突变团竞的游戏视频
                    ...             
12095          一千六百五十三加三千一百六十五点六五等于几
12096                      稍小点客厅空调风速
12097    黎耀祥陈豪邓萃雯畲诗曼陈法拉敖嘉年杨怡马浚伟等到场出席
12098                  百事盖世群星星光演唱会有谁
12099                 下周一视频会议的闹钟帮我开开
Name: 0, Length: 12100, dtype: object
python 复制代码
# 构建数据迭代器
def custom_data_iter(texts,labels):
    for x,y in zip(texts,labels):
        yield x,y

train_iter = custom_data_iter(train_data[0].values[:],train_data[1].values[:])

b. 数据预处理

python 复制代码
# 构建词典
from torchtext.data.utils import get_tokenizer
from torchtext.vocab import build_vocab_from_iterator
import jieba

# 中文分词方法
tokenizer = jieba.lcut # jieba.cut返回的是一个生成器,而jieba.lcut返回的是一个列表

def yield_tokens(data_iter):
    for text,_ in data_iter:
        yield tokenizer(text)
        
vocab = build_vocab_from_iterator(yield_tokens(train_iter),specials=["<unk>"])
vocab.set_default_index(vocab["<unk>"])
复制代码
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\Cheng\AppData\Local\Temp\jieba.cache
Loading model cost 0.549 seconds.
Prefix dict has been built successfully.
python 复制代码
tokenizer('我想看和平精英上战神必备技巧的游戏视频')
复制代码
['我', '想', '看', '和平', '精英', '上', '战神', '必备', '技巧', '的', '游戏', '视频']
python 复制代码
vocab(['我','想','看','和平','精英','上','战神','必备','技巧','的','游戏','视频'])
复制代码
[2, 10, 13, 973, 1079, 146, 7724, 7574, 7793, 1, 186, 28]
python 复制代码
text_pipeline = lambda x:vocab(tokenizer(x))
label_pipeline = lambda x:label_name.index(x)
label_name = list(set(train_data[1].values[:]))
print(label_name)
复制代码
['HomeAppliance-Control', 'Audio-Play', 'Other', 'Weather-Query', 'Music-Play', 'Travel-Query', 'TVProgram-Play', 'Alarm-Update', 'Video-Play', 'Calendar-Query', 'FilmTele-Play', 'Radio-Listen']

python 复制代码
print(text_pipeline('我想看和平精英上战神必备技巧的游戏视频'))
print(label_pipeline('Video-Play'))
复制代码
[2, 10, 13, 973, 1079, 146, 7724, 7574, 7793, 1, 186, 28]
8
python 复制代码
# 生成数据批次和迭代器
from torch.utils.data import DataLoader

def collate_batch(batch):
    label_list,text_list,offsets = [],[],[0]
    
    for (_text,_label) in batch:
        # 标签列表
        label_list.append(label_pipeline(_label))
        # 文本列表
        processed_text = torch.tensor(text_pipeline(_text),dtype = torch.int64)
        text_list.append(processed_text)
        # 偏移量(即语句的总词汇量)
        offsets.append(processed_text.size(0))
        
    label_list = torch.tensor(label_list,dtype = torch.int64)
    text_list = torch.cat(text_list)
    offsets = torch.tensor(offsets[:-1]).cumsum(dim=0) # 返回维度dim中输入元素的累计和
    
    return text_list.to(device),label_list.to(device),offsets.to(device)

# 数据加载器,调用示例
dataloader = DataLoader(train_iter,
                       batch_size = 8,
                       shuffle = False,
                       collate_fn = collate_batch)

c. 模型搭建与初始化

python 复制代码
from torch import nn

class TextClassificationModel(nn.Module):
    
    def __init__(self,vocab_size,embed_dim,num_class):
        super(TextClassificationModel,self).__init__()
        
        self.embedding = nn.EmbeddingBag(vocab_size,  # 词典大小
                                         embed_dim,   # 嵌入维度
                                         sparse=False
                                        )
        self.fc = nn.Linear(embed_dim,num_class)
        self.init_weights()
        
    def init_weights(self):
        initrange = 0.5
        self.embedding.weight.data.uniform_(-initrange,initrange) # 初始化权重
        self.fc.weight.data.uniform_(-initrange,initrange)
        self.fc.bias.data.zero_()
        
    def forward(self,text,offsets):
        embedded = self.embedding(text,offsets)
        return self.fc(embedded)
python 复制代码
# 初始化模型
num_class = len(label_name)
vocab_size = len(vocab)
em_size = 64
model = TextClassificationModel(vocab_size,em_size,num_class).to(device)

d. 训练函数

python 复制代码
import time

def train(dataloader):
    size = len(dataloader.dataset) # 训练集的大小
    num_batches = len(dataloader)  # 批次数目, (size/batch_size,向上取整)
    
    train_acc,train_loss = 0,0 # 初始化训练损失和正确率
    
    for idx,(text,label,offsets) in enumerate(dataloader):
        # 计算预测误差
        predicted_label = model(text,offsets)   # 网络输出
        loss = criterion(predicted_label,label) # 计算网络输出和真实值之间的差距,label为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad() # grad属性归零
        loss.backward() # 反向传播
        torch.nn.utils.clip_grad_norm_(model.parameters(),0.1)
        optimizer.step() # 每一步自动更新
        
        # 记录acc与loss
        train_acc += (predicted_label.argmax(1) == label).sum().item()
        train_loss  += loss.item()
        
    train_acc /= size
    train_loss /= num_batches
    
    return train_acc,train_loss

e. 评估函数

python 复制代码
import time

def evaluate(dataloader):
    test_acc,test_loss,total_count = 0,0,0
    
    with torch.no_grad():
        for idx,(text,label,offsets) in enumerate(dataloader):
            # 计算预测误差
            predicted_label = model(text,offsets)
            loss = criterion(predicted_label,label)
            
            # 记录测试数据
            test_acc += (predicted_label.argmax(1) == label).sum().item()
            test_loss += loss.item()
            total_count += label.size(0)
            
    return test_acc/total_count,test_loss/total_count

f.拆分数据集运行模型

python 复制代码
from torch.utils.data.dataset import random_split
from torchtext.data.functional import to_map_style_dataset
# 超参数
EPOCHS     = 10 # epoch
LR         = 5  # 学习率
BATCH_SIZE = 64 # batch size for training

criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.1)
total_accu = None

# 构建数据集
train_iter = custom_data_iter(train_data[0].values[:], train_data[1].values[:])
train_dataset = to_map_style_dataset(train_iter)

split_train_, split_valid_ = random_split(train_dataset,
                                          [int(len(train_dataset)*0.8),int(len(train_dataset)*0.2)])

train_dataloader = DataLoader(split_train_, batch_size=BATCH_SIZE,
                              shuffle=True, collate_fn=collate_batch)

valid_dataloader = DataLoader(split_valid_, batch_size=BATCH_SIZE,
                              shuffle=True, collate_fn=collate_batch)
python 复制代码
import copy

train_acc = []
train_loss = []
test_acc = []
test_loss = []

best_acc = None # 设置一个最佳准确率,作为最佳模型的判别指标


for epoch in range(1, EPOCHS + 1):
    epoch_start_time = time.time()
    
    model.train() # 切换为训练模式
    epoch_train_acc,epoch_train_loss = train(train_dataloader)
    
    model.eval() # 切换为测试模式
    epoch_test_acc,epoch_test_loss = evaluate(valid_dataloader)
    
    if best_acc is not None and best_acc > epoch_test_acc:
        scheduler.step()
    else:
        best_acc = epoch_test_acc
        best_model = copy.deepcopy(model)
        
    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    
    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']
    
    template = ('Epoch:{:2d},Train_acc:{:.1f}%,Train_loss:{:.3f},Test_acc:{:.1f}%,Test_loss:{:.3f},Lr:{:.2E}')
    print(template.format(epoch,epoch_train_acc*100,epoch_train_loss,epoch_test_acc*100,epoch_test_loss,lr))
    
print('Done!')
复制代码
Epoch: 1,Train_acc:63.8%,Train_loss:1.339,Test_acc:79.5%,Test_loss:0.012,Lr:5.00E+00
Epoch: 2,Train_acc:83.2%,Train_loss:0.592,Test_acc:84.8%,Test_loss:0.008,Lr:5.00E+00
Epoch: 3,Train_acc:88.2%,Train_loss:0.413,Test_acc:86.8%,Test_loss:0.007,Lr:5.00E+00
Epoch: 4,Train_acc:91.1%,Train_loss:0.313,Test_acc:87.6%,Test_loss:0.006,Lr:5.00E+00
Epoch: 5,Train_acc:93.3%,Train_loss:0.241,Test_acc:89.8%,Test_loss:0.006,Lr:5.00E+00
Epoch: 6,Train_acc:95.0%,Train_loss:0.189,Test_acc:89.8%,Test_loss:0.006,Lr:5.00E-01
Epoch: 7,Train_acc:96.7%,Train_loss:0.144,Test_acc:89.6%,Test_loss:0.006,Lr:5.00E-02
Epoch: 8,Train_acc:96.8%,Train_loss:0.139,Test_acc:89.6%,Test_loss:0.006,Lr:5.00E-03
Epoch: 9,Train_acc:96.8%,Train_loss:0.139,Test_acc:89.6%,Test_loss:0.006,Lr:5.00E-04
Epoch:10,Train_acc:96.8%,Train_loss:0.139,Test_acc:89.6%,Test_loss:0.005,Lr:5.00E-05
Done!

g. 结果可视化

python 复制代码
epochs_range = range(EPOCHS)

plt.figure(figsize=(12,3))
plt.subplot(1,2,1)

plt.plot(epochs_range,train_acc,label='Training Accuracy')
plt.plot(epochs_range,test_acc,label='Test Accuracy')
plt.legend(loc = 'lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1,2,2)
plt.plot(epochs_range,train_loss,label='Train Loss')
plt.plot(epochs_range,test_loss,label='Test Loss')
plt.legend(loc = 'lower right')
plt.title('Training and Validation Loss')
plt.show()


h. 测试指定数据

注意以下俩种测试方法,必须保证模型和数据在同样的设备上(GPU或CPU)

python 复制代码
def predict(text, text_pipeline):
    with torch.no_grad():
        text = torch.tensor(text_pipeline(text))
        output = model(text, torch.tensor([0]))
        return output.argmax(1).item()

# ex_text_str = "随便播放一首专辑阁楼里的佛里的歌"
ex_text_str = "还有双鸭山到淮阴的汽车票吗13号的"

model = model.to("cpu")

print("该文本的类别是:%s" %label_name[predict(ex_text_str, text_pipeline)])
复制代码
该文本的类别是:Travel-Query

python 复制代码
def predict(text, text_pipeline, model, device):
    model.eval()  # 切换为评估模式
    with torch.no_grad():
        # 将文本和偏移量张量都移动到相同的设备
        text = torch.tensor(text_pipeline(text)).to(device)
        offsets = torch.tensor([0]).to(device)
        output = model(text, offsets)
        return output.argmax(1).item()

ex_text_str = "还有双鸭山到淮阴的汽车票吗13号的"

# 确保模型在设备上
model = model.to(device)

print("该文本的类别是:%s" % label_name[predict(ex_text_str, text_pipeline, model, device)])
复制代码
该文本的类别是:Travel-Query

2. TextCNN(通用模型-待拓展)

python 复制代码

3. Bert(高级模型-待拓展)

python 复制代码
相关推荐
ShallWeL8 分钟前
【机器学习】(40)—— 流水线监控
人工智能·机器学习·流水线监控
小白勇闯网安圈17 分钟前
Django Ajax、批量操作与分页实践
python·django
小易测试笔记23 分钟前
芯片ESD测试仪是什么?主要有哪几种不同类型?
人工智能
Logintern0931 分钟前
Langgraph使用MemorySaver建立有记忆的图
开发语言·windows·python
counting money31 分钟前
Spring AI Alibaba 从入门到实战:构建企业级 AI 应用
java·人工智能·spring
科技每日热闻31 分钟前
AI 赋能财务资金管理、风险预警、经营数据分析,合适的云上财务智能 Agent 有哪些?—— 优先采用 Amazon Quick 四链路解决方案
人工智能·ai·数据挖掘·数据分析
CAD老兵33 分钟前
让 AI Coding Agent 直接访问 CAD 文档:GitMCP 实战指南
前端·人工智能·github
Linguwen36 分钟前
外贸GEO06|AI搜索引擎怎么工作?理解原理才能做好优化
人工智能·搜索引擎
DS随心转APP39 分钟前
生成word文档的DeepSeek底层解码与“AI导出鸭”工程化方案:一份技术架构师的全维度测评
人工智能·ai·chatgpt·word·deepseek·ai导出鸭
小猴子下山12343 分钟前
2026年无锡滨湖区健康管理公司解析:全周期服务与细胞技术如何匹配需求
人工智能·精选