深度学习实战之简单文本分类

问题描述

在上一次实战中介绍了深度学习找最大数字,这次将数字换成字符,实现list.index()的功能,同时也可以多理解embeding层和RNN层,这次举例的字符是“收藏点击加关注zeon3pang”,随机不重复(其实重复也可以做)的从这几个字符中抽取4个字符,“赞”字在第几个就将它归为第几类,如果没有抽到这个字就将他归为0类。步骤和上次实战类似,只不过对于自然语言的处理,相比上次多了一个词表的建立,所以第一步先建立一个词表。

步骤1:定义词表
步骤2:生成训练集
步骤3:定义神经网络
步骤4:代入数据训练参数
步骤5:模型评估预测

定义词表

import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt


def build_vocab():
    chars = "点赞收藏加关注zeon3pang"  #字符集
    vocab = {"pad":0}
    for index, char in enumerate(chars):
        vocab[char] = index+1   #每个字对应一个序号
    vocab['unk'] = len(vocab)
    return vocab

对于词表,将字符存入字典里,每个字符对应有个数字,这样就将字符数字化了,而"pad"的存储分别对应着的是样本不一致时,将空缺的样本补充为0,这次实战用不到,因为我们每次都是抽取4个字符,所以样本都是相同长度的。"unk"的存储是对应着样本的字符不在词表里的一个问题,不过我们这次也用不到。这次只是先做一个了解。

生成训练集

def build_sample(vocab, sentence_length):
    #随机从字表选取sentence_length个字
    x = random.sample(list(vocab.keys()),sentence_length)
    #指定样本分类
    if '赞' in x:
        y=x.index('赞')+1
    else:
        y=0
    x = [vocab.get(word, vocab['unk']) for word in x]   #将字转换成序号,为了做embedding
    return x, y

def build_dataset(sample_length, vocab, sentence_length):
    dataset_x = []
    dataset_y = []
    for i in range(sample_length):
        x, y = build_sample(vocab, sentence_length)
        dataset_x.append(x)
        dataset_y.append(y)
    return torch.LongTensor(dataset_x), torch.LongTensor(dataset_y)

这里的样本生成与上次基本差不多,为一的区别是上次定义的 y y y值是一个列表,这次我们直接用一个数值来表示样本的类别。

定义神经网络

神经网络第一层肯定是就是embedding层了,虽说我们对每个字都制定了对应的数字,但是我们每个数字还需要对应一个向量,向量维数随便取个20维,然后通过反向传播来对每个字符的向量不断训练。对于第二层就是我们的RNN层了,因为我们的任务是一个序列任务,所以是需要经过RNN层的,而且RNN层有一个池化的作用,这里可以看我的这篇介绍深度学习之RNN。可以将前面的层作为一个特征工程,最后是一个线性层,然后他是一个多分类的问题,然后我们通过交叉熵损失函数进行一个反向传播。这样就完成了我们的神经网络的搭建。实例图如下:
在这里插入图片描述
这里因为我们需要判断五类,所以输出类别是5个类别,对应搭建的代码如下:

class TorchModel(nn.Module):
    def __init__(self, vector_dim, sentence_length, vocab):
        super(TorchModel, self).__init__()
        self.embedding = nn.Embedding(len(vocab), vector_dim,padding_idx=0)  #embedding层
        self.classify = nn.Linear(5, 5)
        self.rnn = nn.RNN(vector_dim,5,batch_first=True)
        self.loss = nn.functional.cross_entropy

    #当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.embedding(x)
        x = self.rnn(x)[1]
        x = x.squeeze()#经过RNN后会多一个维度,所以去掉一个维度
        y_pred = self.classify(x)
        if y is not None:
            return self.loss(y_pred, y)   #预测值和真实值计算损失
        else:
            return y_pred                 #输出预测结果

代入数据训练参数

def build_model(vocab, char_dim, sentence_length):
    model = TorchModel(char_dim, sentence_length, vocab)
    return model

def evaluate(model, vocab, sample_length):
    model.eval()
    x, y = build_dataset(200, vocab, sample_length)   #建立200个用于测试的样本
    correct, wrong = 0, 0
    with torch.no_grad():
        y_pred = model(x)      #模型预测
        for y_p, y_t in zip(y_pred, y):  #与真实标签进行对比
            if torch.argmax(y_p)==int(y_t) :
                correct += 1   #负样本判断正确
            else:
                wrong += 1
    print("正确预测个数:%d, 正确率:%f"%(correct, correct/(correct+wrong)))
    return correct/(correct+wrong)

def main():
    #配置参数
    epoch_num = 20        #训练轮数
    batch_size = 20       #每次训练样本个数
    train_sample = 500    #每轮训练总共训练的样本总数
    char_dim = 20         #每个字的维度
    sentence_length = 4   #样本文本长度
    learning_rate = 0.005 #学习率
    # 建立字表
    vocab = build_vocab()
    # 建立模型
    model = build_model(vocab, char_dim, sentence_length)
    # 选择优化器
    optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
    log = []
    # 训练过程
    for epoch in range(epoch_num):
        model.train()
        watch_loss = []
        for batch in range(int(train_sample / batch_size)):
            x, y = build_dataset(batch_size, vocab, sentence_length) #构造一组训练样本
            optim.zero_grad()    #梯度归零
            loss = model(x, y)   #计算loss
            loss.backward()      #计算梯度
            optim.step()         #更新权重
            watch_loss.append(loss.item())
        print("=========\n第%d轮平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
        acc = evaluate(model, vocab, sentence_length)   #测试本轮模型结果
        log.append([acc, np.mean(watch_loss)])
    #画图
    plt.plot(range(len(log)), [l[0] for l in log], label="acc")  #画acc曲线
    plt.plot(range(len(log)), [l[1] for l in log], label="loss")  #画loss曲线
    plt.legend()
    plt.show()
    #保存模型
    torch.save(model.state_dict(), "model.pth")
    # 保存词表
    writer = open("vocab.json", "w", encoding="utf8")
    writer.write(json.dumps(vocab, ensure_ascii=False, indent=2))
    writer.close()
    return
main()    

训练部分代码比较简单,基本上和上次基本差不多,定义学习率维度一些参数,不同的是y值定义与上次不同,所以评估部分的代码比上次更加简洁一些,这里直接看结果:
在这里插入图片描述
这里有上次的调参经验,最终训练结果预测率准确率为百分百,说明他也是一个比较简单语言分类任务。

模型评估预测

def predict(model_path, vocab_path, input_strings):
    char_dim = 20  # 每个字的维度
    sentence_length = 4  # 样本文本长度
    vocab = json.load(open(vocab_path, "r", encoding="utf8")) #加载字符表
    model = build_model(vocab, char_dim, sentence_length)     #建立模型
    model.load_state_dict(torch.load(model_path))             #加载训练好的权重
    x = []
    for input_string in input_strings:
        x.append([vocab[char] for char in input_string])  #将输入序列化
    model.eval()   #测试模式
    with torch.no_grad():  #不计算梯度
        result = model.forward(torch.LongTensor(x))  #模型预测
    for i,input_string in enumerate(result):
        print("输入:%s, 预测类别:%d" % (input_strings[i], int(input_string.argmax()))) #打印结果
test_strings = ["zeon", "点赞收藏", "收藏点赞", "关注收藏"]
predict("model.pth", "vocab.json", test_strings)

模型评估预测也是与上次代码相同,这里尝试的字符有"zeon", “点赞收藏”, “收藏点赞”, “关注收藏”,这几个,直接看结果:
在这里插入图片描述
预测结果都正确。

思考

做完这个小实战,突然想起上次一个建筑行业的数据分析面试题目,问:我们的数据因为都是人工输入,有些人因为输入的单位不同,所以他们输入的都是文本类型,怎么整合这个数据,让他变得统一?虽然好像可以通过一个简单的循环处理,但也可以通过这个模型处理。

  • 31
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zeon3paang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值