Pytorch框架—文本情感分类问题项目(二)

整体过程就是首先拿到了数据集微博100K,对个这个评论数据集进行处理,分类标签和评论内容。对评论内容进行分词处理,之后进行词频统计对高词频的进行编码,低词频的进用《UNK》表示,并使用《PAD》把他们扩展到等长列表便于处理。

拿到处理好的数据集,将其输入通过嵌入层得到密集表示向量表示,之后通过LSTM层,激活函数层,池化层,全连接层,最后应用softmax激活函数,得到类别概率。

1 数据准备

训练集 微博评论100K

停用词 采用合工大停用词表

对数据进行处理->拿到字典->利用字典对语料信息进行编码

1 读取评论和停用词文件

2 对评论内容进行分词处理并进行停用词过滤操作

3 根据分词结果统计词典

4 对词典进行排序 取top n的词作为最终词典,其余词作为同一个字符(因为不重要,提高运算速度)(UNK,PAD放最后)

data_processing.py

# coding=utf-8

import jieba  # jieba中文分词使用

# 微博评论数据路径
data_path = "sources/weibo_senti_100k.csv"
# 停用词路径
data_stop_path = "sources/hit_stopword.txt"
# 读取文件中的每一行,并将每一行作为一个字符串存储在一个列表中,跳过第一行
data_list = open(data_path, encoding="utf-8").readlines()[1:]
# 拿到停用词列表
stops_word = open(data_stop_path, encoding="utf-8").readlines()
# 过滤停用词换行符
stops_word = [line.strip() for line in stops_word]
# 手动添加过滤空格和换行
stops_word.append(" ")
stops_word.append("\n")

# 定义统计字典
voc_dict = {}
# 字典中的最小词频数
min_seq = 1
# 取字典中前top_n个
top_n = 1000
# 对于前1000以为的值为UNK
UNK = "<UNK>"
# NLP建模需要PAD到固定长度
PAD = "<PAD>"

# 对每条评论进行分词处理
for item in data_list:

    # 标签 1代表积极 0代表消极
    label = item[0]

    # 评论内容
    content = item[2:].strip()  # .strip() 方法去除前后的空格。

    # 使用jieba进行分词  例:梦想 有 多 大 , 舞台 就 有 多 大 ! 鼓掌
    seg_list = jieba.cut(content, cut_all=False)

    # 去除停用词后的最终分词结果
    seg_res = []

    # 去除停用词并创建词频字典
    for seg_item in seg_list:

        # 判断分词词语是否为停用词
        if (seg_item in stops_word):
            continue
        seg_res.append(seg_item)
        # seg_res : ['梦想', '大', '舞台', '大', '!', '鼓掌']
        # 判断该词语是否在字典中
        if seg_item in voc_dict.keys():
            voc_dict[seg_item] = voc_dict[seg_item] + 1  # 在的话词频+1
        else:
            voc_dict[seg_item] = 1  # 不在就加进字典去

    # voc_dict:{'\ufeff': 1, '更博': 1, '爆照': 2, '帅': 3, '越来越': 1, '爱': 4, '生快': 1, '傻': 1, '缺': 1}

# 对字典进行排序
voc_list = sorted([_ for _ in voc_dict.items() if _[1] > min_seq],
                  key=lambda x: x[1],  # key=lambda x:x[1]:指定排序的依据是每个项中的第二个元素(即值)。
                  reverse=True)[:top_n]
"""
for idx, word_count in enumerate(voc_list):
    print(idx," ",word_count)
    
0   ('爱', 6)
1   ('都', 2)
2   ('大', 2)
"""

# 将处理后的字典重新赋值给voc_dict
voc_dict = {word_count[0]: idx for idx, word_count in enumerate(voc_list)}  # enumerate得到voc_list的键值和索引值

# UNK:len(voc_dict) 表示将 UNK 这个词映射到一个特定的索引值 len(voc_dict)。这意味着,如果在后续的处理中遇到 UNK,它将被视为一个有效的词汇,并被分配一个特定的索引。
# PAD:len(voc_dict)+1 类似地,将 PAD 映射到一个比 voc_dict 中最后一个词汇的索引大 1 的值。这通常用于填充序列,使其达到固定的长度。
voc_dict.update({UNK: len(voc_dict), PAD: len(voc_dict) + 1})

# 保存字典
ff = open("sources/dict", "w", encoding="utf-8")
for item in voc_dict.keys():
    ff.writelines("{},{}\n".format(item, voc_dict[item]))
ff.close()

dataset类的定义 自定义训练模型的数据类

作用 利用字典对词进行编码操作 拿到编码后的数据集

1 实现text_CLS(Dataset):类,包括初始化,获取长度,获得编码后的分词数据集

2 实现初始化

   通过load_data对数据集进行分词,过滤停用词,拿到数据集的数据字典和最大分词长度

3 实现获取长度

 获得数据集长度

4 实现获得编码后的分词数据集

编码是自己之保存了词频超过1的,词频为1的用UNK表示,为了统一长度用PAD的编码填充。

datasets.py

from torch.utils.data import Dataset, DataLoader  # 从 torch 库中导入数据集(Dataset)和数据加载器(DataLoader)类
import jieba  # 导入中文分词库 jieba
import numpy as np  # 导入科学计算库 numpy


# 在 PyTorch 中,Dataset 是一个抽象类,用于表示数据集。它提供了一种统一的接口,用于读取和处理数据,
# 并与 DataLoader 结合使用,实现数据的批处理和迭代加载。

# 读取字典
def read_dict(voc_dict_path):
    voc_dict = {}

    # 读取字典中的内容 {泪,0 嘻嘻,1 都,2 爱,3...}
    dict_list = open(voc_dict_path, encoding="utf-8").readlines()

    for item in dict_list:

        item = item.split(",")
        voc_dict[item[0]] = int(item[1].strip())
    # 拿到字典
    return voc_dict

#加载数据
#获得 [['1', ['\ufeff', '更博', '爆照', '帅', '越来越', '爱', '生快', '傻', '缺', '爱', '爱', '爱']], ['1', ['张晓鹏', 'jonathan', '土耳其', '事要', '认真对待', '直接', '开除', '丁丁', '看', '世界', '很', '细心', '酒店', '都', '全部', 'OK']]]
#和 最大的字典项长度
def load_data(data_path, data_stop_path):
    # 读取文件中的每一行,并将每一行作为一个字符串存储在一个列表中,跳过第一行
    data_list = open(data_path, encoding="utf-8").readlines()[1:]
    # 拿到停用词列表
    stops_word = open(data_stop_path, encoding="utf-8").readlines()
    # 过滤停用词换行符
    stops_word = [line.strip() for line in stops_word]
    # 手动添加过滤空格和换行
    stops_word.append(" ")
    stops_word.append("\n")

    # 定义统计字典
    voc_dict = {}
    # 定义存储最终数据
    data = []
    # 最长句子的长度
    max_len_seq = 0

    # 对每条评论进行分词处理
    for item in data_list:

        # 标签 1代表积极 0代表消极
        label = item[0]

        # 评论内容
        content = item[2:].strip()  # .strip() 方法去除前后的空格。

        # 使用jieba进行分词  例:梦想 有 多 大 , 舞台 就 有 多 大 ! 鼓掌
        seg_list = jieba.cut(content, cut_all=False)

        # 去除停用词后的最终分词结果
        seg_res = []

        # 去除停用词并创建词频字典
        for seg_item in seg_list:

            # 判断分词词语是否为停用词
            if (seg_item in stops_word):
                continue
            seg_res.append(seg_item)
            # seg_res : ['梦想', '大', '舞台', '大', '!', '鼓掌']
            # 判断该词语是否在字典中
            if seg_item in voc_dict.keys():
                voc_dict[seg_item] = voc_dict[seg_item] + 1  # 在的话词频+1
            else:
                voc_dict[seg_item] = 1  # 不在就加进字典去

        # voc_dict:{'\ufeff': 1, '更博': 1, '爆照': 2, '帅': 3, '越来越': 1, '爱': 4, '生快': 1, '傻': 1, '缺': 1}

        # 记录最长句子的长度  #将句子PAD到固定的长度 以最长的句子为基准
        if len(seg_res) > max_len_seq:
            max_len_seq = len(seg_res)
        # 添加到数据集data  [['1', ['\ufeff', '更博', '爆照', '帅', '越来越', '爱', '生快', '傻', '缺', '爱', '爱', '爱']], ['1', ['张晓鹏', 'jonathan', '土耳其', '事要', '认真对待', '直接', '开除', '丁丁', '看', '世界', '很', '细心', '酒店', '都', '全部', 'OK']]]
        data.append([label, seg_res])
        # print(data)
    return data, max_len_seq


class text_CLS(Dataset):
    def __init__(self, voc_dict_path, data_path, data_stop_path):
        # 微博评论数据路径
        self.data_path = data_path
        # 停用词路径
        self.data_stop_path = data_stop_path
        # 拿到字典
        self.voc_dict = read_dict(voc_dict_path)
        # 加载训练数据
        self.data, self.max_len_path = load_data(self.data_path, self.data_stop_path)

        # 随机打乱一个数组。
        np.random.shuffle(self.data)

    def __len__(self):
        return len(self.data)

    def __getitem__(self, item):
        data = self.data[item]  # ['1', ['\ufeff', '更博', '爆照', '帅', '越来越', '爱', '生快', '傻', '缺', '爱', '爱', '爱']]
        lable = int(data[0])  # 1
        word_list = data[1]  # ['\ufeff', '更博', '爆照', '帅', '越来越', '爱', '生快', '傻', '缺', '爱', '爱', '爱']
        input_idx = []
        # 将word转化为对应的id
        for word in word_list:
            if word in self.voc_dict.keys():  # 在字典中取字典的编码
                input_idx.append(self.voc_dict[word])
            else:  # 不在取UNK
                input_idx.append(self.voc_dict["<UNK>"])

        # 对齐长度 以最大的为基准
        if len(input_idx) < self.max_len_path:
            input_idx += [self.voc_dict["<PAD>"] for _ in range(self.max_len_path - len(input_idx))]

        # 转为数组
        data = np.array(input_idx)
        return lable, data


def data_loader(dataset,config):
    # # 微博评论数据路径
    # data_path = "sources/weibo_senti_100k.csv"
    # # 停用词路径
    # data_stop_path = "sources/hit_stopword.txt"
    # # 字典路径
    # dict_path = "sources/dict"

    return DataLoader(dataset, batch_size=config.batch_size, shuffle=config.is_shuffle)#将数据集拆分为小批次,并在训练或推理过程中按批次提供数据。

if __name__ == '__main__':
    # 微博评论数据路径
    data_path = "sources/weibo_senti_100k.csv"
    # 停用词路径
    data_stop_path = "sources/hit_stopword.txt"
    # 字典路径
    dict_path = "sources/dict"
    train_dataloader= data_loader(dict_path,data_path,data_stop_path)
    for i,batch in enumerate(train_dataloader):
        print(batch)

config.py 用于配置训练模型

import torch


class Config():
    def __init__(self):
        # 词汇表的大小,表示模型可以处理的不同词汇的数量。
        self.n_vocab = 1002

        # 词嵌入的维度,即将词索引转换为密集向量表示的维度。
        self.embed_size = 128

        # LSTM隐藏状态的维度,决定了模型的记忆容量和表示能力。
        self.hidden_size = 128

        # LSTM层的数量,表示堆叠的LSTM层的层数。
        self.num_layers = 3

        # 用于控制模型中的dropout概率,以减少过拟合。
        self.dropout = 0.8

        # 模型要预测的类别数量。
        self.num_classes = 2

        # 输入序列的固定长度,用于处理变长序列的情况。
        self.pad_size = 32

        self.batch_size=128
        self.is_shuffle=True
        self.numepochs = 100

        #学习率
        self.learn_rate=0.001


        # 用于指定模型在哪个设备上运行,如果CUDA可用,则使用GPU进行加速,否则使用CPU。
        self.devices = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

model.py

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np


class Model(nn.Module):
    # config网络搭建所需要的参数
    def __init__(self, config):
        super(Model, self).__init__()
        # 词嵌入层 将输入序列转化为向量  嵌入层用于将输入的离散化的文本数据转换为连续的稠密向量表示。  嵌入向量通常是随机初始化的,并在训练过程中通过反向传播算法进行优化。
        # config.n_vocab
        # 表示词汇表的大小,即输入文本中不同单词的数量。
        # config.embed_size
        # 表示嵌入向量的维度,即每个单词表示为一个向量的长度。
        # padding_idx = config.n_vocab - 1
        # 表示填充位置的索引,通常用于处理序列长度不一致的情况。这里将最后一个单词的索引设置为填充位置。
        self.embeding = nn.Embedding(config.n_vocab,
                                     config.embed_size,
                                     padding_idx=config.n_vocab - 1)

        # 定义STML层  模型可以对输入的序列数据进行建模和处理,捕捉序列中的长期依赖关系。
        # LSTM 层在训练过程中会学习到如何根据输入的序列数据生成隐藏状态,并在后续的计算中使用这些隐藏状态。
        # 输入为词嵌入层的输出,即词向量化之后的结果
        self.lstm = nn.LSTM(config.embed_size,
                            config.hidden_size,  # 隐藏层节点的数量
                            config.num_layers,  # 使用几个LSTM
                            bidirectional=True,  # 双向STML
                            batch_first=True,  # 表示输入数据的批次维度位于第一个位置
                            dropout=config.dropout)  # 随机抑制一些节点,防止过拟合

        # 定义卷积网络
        # 是一个实例变量,用于创建一个最大池化层(MaxPool1d)。最大池化层用于对输入数据进行下采样,减少特征图的尺寸。
        self.maxPool = nn.MaxPool1d(config.pad_size)  # config.pad_size表示池化操作中的填充大小,用于处理输入序列长度不一致的情况。

        # 定义fc层即线性层 用于创建一个全连接层(Linear)。全连接层用于将之前的特征映射转换为分类输出。
        self.fc = nn.Linear(config.hidden_size * 2 + config.embed_size,
                            # 表示全连接层的输入维度,由 LSTM 层的隐藏状态维度乘以 2(因为是双向 LSTM)加上词嵌入层的输出维度组成。
                            config.num_classes)  # 表示分类任务的类别数量,即全连接层的输出维度。

        # Softmax 层用于将全连接层的输出转换为概率分布,表示每个类别的预测概率。
        self.softmax = nn.Softmax(dim=1)

    # 模型的前向传播方法,它描述了数据在模型中的流动过程
    def forward(self, x):
        # 输出结果[batch_size,seqlen,embed_size]
        embed = self.embeding(x)  # 将输入通过嵌入层得到密集表示

        out, _ = self.lstm(embed)  # 将嵌入的输入通过LSTM层得到输出序列

        out = torch.cat((embed, out), 2)  # 沿特征维度连接嵌入的输入和LSTM输出

        out = F.relu(out)  # 应用ReLU激活函数

        out = out.permute(0, 2, 1)  # 调整张量的维度以进行最大池化操作

        out = self.maxPool(out).reshape(out.size()[0], -1)  # 执行最大池化操作,获得固定大小的可变长度输入表示

        out = self.fc(out)  # 将池化输出通过全连接层

        out = self.softmax(out)  # 应用softmax激活函数,得到类别概率

        return out


if __name__ == '__main__':
    from configs import Config

    cfg = Config()
    cfg.pad_size = 640  # 这个参数用于指定输入序列的固定长度。
    model_textcls = Model(config=cfg)
    # 创建一个输入张量input_tensor,包含从0到639的索引序列,并将其reshape为形状为[1, 640]的张量。这里使用了列表推导式来生成索引序列。
    input_tensor = torch.tensor([i for i in range(640)]).reshape([1, 640])
    out_tensor = model_textcls.forward(input_tensor)
    print(out_tensor.size())
    print(out_tensor)

run_train.py

import torch
import torch.nn as nn
from torch import optim  # 导入优化器
from models import Model  # 导入模型
from datasets import data_loader, text_CLS  # 导入数据
from configs import Config  # 导入配置

# 创建一个配置对象cfg
cfg = Config()

# 微博评论数据路径
data_path = "sources/weibo_senti_100k.csv"
# 停用词路径
data_stop_path = "sources/hit_stopword.txt"
# 字典路径
dict_path = "sources/dict"

dataset = text_CLS(dict_path, data_path, data_stop_path)
# 使用data_loader函数创建一个训练数据的数据加载器train_dataloader,用于按批次加载数据。
train_dataloader = data_loader(dataset, cfg)

# 将配置对象cfg的pad_size属性设置为数据集的最大序列长度。
cfg.pad_size = dataset.max_len_path

# 创建一个模型对象model_text_cls,并将其移动到指定的设备(GPU或CPU)上进行计算。
model_text_cls = Model(config=cfg)
model_text_cls.to(cfg.devices)

# 定义损失函数 采用交叉熵
loss_func = nn.CrossEntropyLoss()
# 定义优化器
optimizer = optim.Adam(model_text_cls.parameters(), lr=cfg.learn_rate)

flag = 1
# 开始训练
"""
使用嵌套的循环进行模型的训练。外层循环迭代训练的轮数(numepochs),内层循环迭代每个批次的数据。
在每个批次中,将数据和标签转换为张量并移动到指定的设备上。然后,将模型的梯度清零(zero_grad()),
通过调用模型的前向传播方法(forward())获取预测结果,并计算损失值。
接着,通过调用损失函数的backward()方法进行反向传播,计算梯度并更新模型的参数(step())。
"""
for epoch in range(cfg.numepochs):
    for i, batch in enumerate(train_dataloader):  # 使用enumerate函数获取批次的索引i和对应的数据batch。
        label, data = batch

        # 将数据和标签转换为张量,并将它们移动到指定的设备上(GPU或CPU)。
        data = torch.tensor(data).to(cfg.devices)
        label = torch.tensor(label, dtype=torch.int64).to(cfg.devices)

        optimizer.zero_grad()  # 将优化器的梯度缓冲区清零,以便进行下一次反向传播。
        pred = model_text_cls.forward(data)  # 通过调用模型的前向传播方法,将数据输入模型获得预测结果。
        loss_val = loss_func(pred, label)  # 使用损失函数计算预测结果与标签之间的损失值。
        # 打印预测结果
        # print(pred)
        # print(label)
        # print("epoch is {} ,ite is {},val is {}".format(epoch, i, loss_val))
        loss_val.backward()  # 执行反向传播,计算梯度。
        optimizer.step()  # 根据计算得到的梯度,更新模型的参数。

    print(f"完成{flag}次迭代{epoch}")
    flag += 1

    if epoch % 20 == 0:
        print("保存了{}".format(epoch))
        torch.save(model_text_cls.state_dict(), "models/{}.pth".format(epoch))

print("训练完成")

test.py

import torch
import torch.nn as nn
from torch import optim  # 导入优化器
from models import Model  # 导入模型
from datasets import data_loader, text_CLS  # 导入数据
from configs import Config  # 导入配置
#创建一个配置对象cfg
cfg = Config()

# 微博评论数据路径
data_path = "sources/weibo_senti_100k.csv"
# 停用词路径
data_stop_path = "sources/hit_stopword.txt"
# 字典路径
dict_path = "sources/dict"


dataset = text_CLS(dict_path, data_path, data_stop_path)
#使用data_loader函数创建一个训练数据的数据加载器train_dataloader,用于按批次加载数据。
train_dataloader = data_loader(dataset, cfg)

#将配置对象cfg的pad_size属性设置为数据集的最大序列长度。
cfg.pad_size = dataset.max_len_path

# 创建一个模型对象model_text_cls,并将其移动到指定的设备(GPU或CPU)上进行计算。
model_text_cls = Model(config=cfg)
model_text_cls.to(cfg.devices)

model_text_cls.load_state_dict(torch.load("models"))


# 开始训练
"""
使用嵌套的循环进行模型的训练。外层循环迭代训练的轮数(numepochs),内层循环迭代每个批次的数据。
在每个批次中,将数据和标签转换为张量并移动到指定的设备上。然后,将模型的梯度清零(zero_grad()),
通过调用模型的前向传播方法(forward())获取预测结果,并计算损失值。
接着,通过调用损失函数的backward()方法进行反向传播,计算梯度并更新模型的参数(step())。
"""

for i, batch in enumerate(train_dataloader): #使用enumerate函数获取批次的索引i和对应的数据batch。
    label, data = batch

    #将数据和标签转换为张量,并将它们移动到指定的设备上(GPU或CPU)。
    data = torch.tensor(data).to(cfg.devices)
    label = torch.tensor(label, dtype=torch.int64).to(cfg.devices)
    pred_softmax = model_text_cls.forward(data) #通过调用模型的前向传播方法,将数据输入模型获得预测结果。

    # 打印预测结果
    # print(pred_softmax)
    # print(label)
    # print("epoch is {} ,ite is {},val is {}".format(epoch, i, loss_val))
    pred=torch.argmax(pred_softmax,dim=1)
    # print(pred)

    out= torch.eq(pred,label)
    # print(out)
    print(out.sum()*1.0/pred.size()[0])

智能网联汽车的安全员高级考试涉及多个方面的专业知识,包括但不限于自动驾驶技术原理、车辆传感器融合、网络安全防护以及法律法规等内容。以下是针对该主题的一些核心知识点解析: ### 关于智能网联车安全员高级考试的核心内容 #### 1. 自动驾驶分级标准 国际自动机工程师学会(SAE International)定义了六个级别的自动驾驶等级,从L0到L5[^1]。其中,L3及以上级别需要安全员具备更高的应急处理能力。 #### 2. 车辆感知系统的组成与功能 智能网联车通常配备多种传感器,如激光雷达、毫米波雷达、摄像头和超声波传感器等。这些设备协同工作以实现环境感知、障碍物检测等功能[^2]。 #### 3. 数据通信与网络安全 智能网联车依赖V2X(Vehicle-to-Everything)技术进行数据交换,在此过程中需防范潜在的网络攻击风险,例如中间人攻击或恶意软件入侵[^3]。 #### 4. 法律法规要求 不同国家和地区对于无人驾驶测试及运营有着严格的规定,考生应熟悉当地交通法典中有关自动化驾驶部分的具体条款[^4]。 ```python # 示例代码:模拟简单决策逻辑 def decide_action(sensor_data): if sensor_data['obstacle'] and not sensor_data['emergency']: return 'slow_down' elif sensor_data['pedestrian_crossing']: return 'stop_and_yield' else: return 'continue_driving' example_input = {'obstacle': True, 'emergency': False, 'pedestrian_crossing': False} action = decide_action(example_input) print(f"Action to take: {action}") ``` 需要注意的是,“橙点同学”作为特定平台上的学习资源名称,并不提供官方认证的标准答案集;建议通过正规渠道获取教材并参加培训课程来准备此类资格认证考试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值