语音情感分类(1)简单可运行项目(附代码)

1.目标

题主最开始是想做一个音乐情感分类的模型,但是查阅相关文献发现这个范围太大了,音乐情感特征包括文本,音频,甚至有的还有画面,是一个多模态的范畴。所以退而求其次,找了一个接近的语音情感分类来学习。主要是学习所使用的数据集,数据集格式,一些经典的模型,为后续进一步学习打基础。

2.LSTM

找了很久在码云上找到的一个项目,代码结构很清晰,也有注释,用来学习很适合不过。
代码地址:https://github.com/frank330/Emotion_analysis/tree/main

接下来将以LSTM模型为例来讲述语音情感分类模型代码

2.1 代码目录

data文件中存放的是数据集相关文件,result文件中存放的是训练结果,saved.dict文件存放的是训练好的模型。main.py是唯一可执行文件,代码中包含参数设置,数据获取,模型定义,训练设置等。

代码运行界面

2.2 具体代码

# -*- coding: utf-8 -*-
import numpy as np
import pickle as pkl
from tqdm import tqdm
from datetime import timedelta
from torch.utils.data import Dataset, DataLoader
import torch.nn as nn
import time
import torch
from sklearn import metrics
from sklearn.model_selection import train_test_split
import seaborn as sns
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
from sklearn.metrics import precision_score, accuracy_score, f1_score, recall_score# 超参数设置
data_path =  './data/data.txt'              # 数据集
vocab_path = './data/vocab.pkl'             # 词表
save_path = './saved_dict/lstm.ckpt'        # 模型训练结果
embedding_pretrained = \torch.tensor(np.load('./data/embedding_Tencent.npz')["embeddings"].astype('float32'))# 预训练词向量
embed = embedding_pretrained.size(1)        # 词向量维度
dropout = 0.5                               # 随机丢弃
num_classes = 2                             # 类别数
num_epochs = 30                             # epoch数
batch_size = 128                            # mini-batch大小
pad_size = 50                               # 每句话处理成的长度(短填长切)
learning_rate = 1e-3                        # 学习率
hidden_size = 128                           # lstm隐藏层
num_layers = 2                              # lstm层数
MAX_VOCAB_SIZE = 10000                      # 词表长度限制
UNK, PAD = '<UNK>', '<PAD>'                 # 未知字,padding符号def get_data():tokenizer = lambda x: [y for y in x]  # 字级别vocab = pkl.load(open(vocab_path, 'rb'))# print('tokenizer',tokenizer)print('vocab',vocab)print(f"Vocab size: {len(vocab)}")train,dev,test = load_dataset(data_path, pad_size, tokenizer, vocab)return vocab, train, dev, testdef load_dataset(path, pad_size, tokenizer, vocab):'''将路径文本文件分词并转为三元组返回:param path: 文件路径:param pad_size: 每个序列的大小:param tokenizer: 转为字级别:param vocab: 词向量模型:return: 二元组,含有字ID,标签'''contents = []n=0with open(path, 'r', encoding='gbk') as f:# tqdm可以看进度条for line in tqdm(f):# 默认删除字符串line中的空格、’\n’、't’等。lin = line.strip()if not lin:continue# print(lin)label,content = lin.split('	####	')# word_line存储每个字的idwords_line = []# 分割器,分词每个字token = tokenizer(content)# print(token)# 字的长度seq_len = len(token)if pad_size:# 如果字长度小于指定长度,则填充,否则截断if len(token) < pad_size:token.extend([vocab.get(PAD)] * (pad_size - len(token)))else:token = token[:pad_size]seq_len = pad_size# 将每个字映射为ID# 如果在词表vocab中有word这个单词,那么就取出它的id;# 如果没有,就去除UNK(未知词)对应的id,其中UNK表示所有的未知词(out of vocab)都对应该idfor word in token:words_line.append(vocab.get(word, vocab.get(UNK)))n+=1contents.append((words_line, int(label)))train, X_t = train_test_split(contents, test_size=0.4, random_state=42)dev,test= train_test_split(X_t, test_size=0.5, random_state=42)return train,dev,test
# get_data()class TextDataset(Dataset):def __init__(self, data):self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')self.x = torch.LongTensor([x[0] for x in data]).to(self.device)self.y = torch.LongTensor([x[1] for x in data]).to(self.device)def __getitem__(self,index):self.text = self.x[index]self.label = self.y[index]return self.text, self.labeldef __len__(self):return len(self.x)# 以上是数据预处理的部分def get_time_dif(start_time):"""获取已使用时间"""end_time = time.time()time_dif = end_time - start_timereturn timedelta(seconds=int(round(time_dif)))# 定义LSTM模型
class Model(nn.Module):def __init__(self):super(Model, self).__init__()# 使用预训练的词向量模型,freeze=False 表示允许参数在训练中更新# 在NLP任务中,当我们搭建网络时,第一层往往是嵌入层,对于嵌入层有两种方式初始化embedding向量,# 一种是直接随机初始化,另一种是使用预训练好的词向量初始化。self.embedding = nn.Embedding.from_pretrained(embedding_pretrained, freeze=False)# bidirectional=True表示使用的是双向LSTMself.lstm = nn.LSTM(embed, hidden_size, num_layers,bidirectional=True, batch_first=True, dropout=dropout)# 因为是双向LSTM,所以层数为config.hidden_size * 2self.fc = nn.Linear(hidden_size * 2, num_classes)def forward(self, x):out = self.embedding(x)# lstm 的input为[batchsize, max_length, embedding_size],输出表示为 output,(h_n,c_n),# 保存了每个时间步的输出,如果想要获取最后一个时间步的输出,则可以这么获取:output_last = output[:,-1,:]out, _ = self.lstm(out)out = self.fc(out[:, -1, :])  # 句子最后时刻的 hidden statereturn out
def get_time_dif(start_time):"""获取已使用时间"""end_time = time.time()time_dif = end_time - start_timereturn timedelta(seconds=int(round(time_dif)))# 权重初始化,默认xavier
# xavier和kaiming是两种初始化参数的方法
def init_network(model, method='xavier', exclude='embedding'):for name, w in model.named_parameters():if exclude not in name:if 'weight' in name:if method == 'xavier':nn.init.xavier_normal_(w)elif method == 'kaiming':nn.init.kaiming_normal_(w)else:nn.init.normal_(w)elif 'bias' in name:nn.init.constant_(w, 0)else:passdef plot_acc(train_acc):sns.set(style='darkgrid')plt.figure(figsize=(10, 7))x = list(range(len(train_acc)))plt.plot(x, train_acc, alpha=0.9, linewidth=2, label='train acc')plt.xlabel('Epoch')plt.ylabel('Acc')plt.legend(loc='best')plt.savefig('results/acc.png', dpi=400)def plot_loss(train_loss):sns.set(style='darkgrid')plt.figure(figsize=(10, 7))x = list(range(len(train_loss)))plt.plot(x, train_loss, alpha=0.9, linewidth=2, label='train acc')plt.xlabel('Epoch')plt.ylabel('loss')plt.legend(loc='best')plt.savefig('results/loss.png', dpi=400)# 定义训练的过程
def train( model, dataloaders):'''训练模型:param model: 模型:param dataloaders: 处理后的数据,包含trian,dev,test'''optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)loss_function = torch.nn.CrossEntropyLoss()dev_best_loss = float('inf')device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')print("Start Training...\n")plot_train_acc = []plot_train_loss = []for i in range(num_epochs):# 1,训练循环----------------------------------------------------------------# 将数据全部取完# 记录每一个batchstep = 0train_lossi=0train_acci = 0for inputs, labels in dataloaders['train']:# 训练模式,可以更新参数model.train()# print(inputs.shape)inputs = inputs.to(device)labels = labels.to(device)# 梯度清零,防止累加optimizer.zero_grad()outputs = model(inputs)loss = loss_function(outputs, labels)loss.backward()optimizer.step()step += 1true = labels.data.cpu()predic = torch.max(outputs.data, 1)[1].cpu()train_lossi += loss.item()train_acci += metrics.accuracy_score(true, predic)# 2,验证集验证----------------------------------------------------------------dev_acc, dev_loss = dev_eval(model, dataloaders['dev'], loss_function,Result_test=False)if dev_loss < dev_best_loss:dev_best_loss = dev_losstorch.save(model.state_dict(), save_path)train_acc = train_acci/steptrain_loss = train_lossi/stepplot_train_acc.append(train_acc)plot_train_loss.append(train_loss)print("epoch = {} :  train_loss = {:.3f}, train_acc = {:.2%}, dev_loss = {:.3f}, dev_acc = {:.2%}".format(i+1, train_loss, train_acc, dev_loss, dev_acc))plot_acc(plot_train_acc)plot_loss(plot_train_loss)# 3,验证循环----------------------------------------------------------------model.load_state_dict(torch.load(save_path))model.eval()start_time = time.time()test_acc, test_loss = dev_eval(model, dataloaders['test'], loss_function,Result_test=True)print('================'*8)print('test_loss: {:.3f}      test_acc: {:.2%}'.format(test_loss, test_acc))def result_test(real, pred):cv_conf = confusion_matrix(real, pred)acc = accuracy_score(real, pred)precision = precision_score(real, pred, average='micro')recall = recall_score(real, pred, average='micro')f1 = f1_score(real, pred, average='micro')patten = 'test:  acc: %.4f   precision: %.4f   recall: %.4f   f1: %.4f'print(patten % (acc, precision, recall, f1,))labels11 = ['negative', 'active']disp = ConfusionMatrixDisplay(confusion_matrix=cv_conf, display_labels=labels11)disp.plot(cmap="Blues", values_format='')plt.savefig("results/reConfusionMatrix.tif", dpi=400)# 模型评估
def dev_eval(model, data, loss_function,Result_test=False):''':param model: 模型:param data: 验证集集或者测试集的数据:param loss_function: 损失函数:return: 损失和准确率'''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:outputs = model(texts)loss = loss_function(outputs, labels)loss_total += loss.item()labels = labels.data.cpu().numpy()predic = torch.max(outputs.data, 1)[1].cpu().numpy()labels_all = np.append(labels_all, labels)predict_all = np.append(predict_all, predic)acc = metrics.accuracy_score(labels_all, predict_all)if Result_test:result_test(labels_all, predict_all)else:passreturn acc, loss_total / len(data)if __name__ == '__main__':# 设置随机数种子,保证每次运行结果一致,不至于不能复现模型np.random.seed(1)torch.manual_seed(1)torch.cuda.manual_seed_all(1)torch.backends.cudnn.deterministic = True  # 保证每次结果一样start_time = time.time()print("Loading data...")vocab, train_data, dev_data, test_data = get_data()dataloaders = {'train': DataLoader(TextDataset(train_data), batch_size, shuffle=True),'dev': DataLoader(TextDataset(dev_data), batch_size, shuffle=True),'test': DataLoader(TextDataset(test_data), batch_size, shuffle=True)}time_dif = get_time_dif(start_time)print("Time usage:", time_dif)device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')model = Model().to(device)init_network(model)train(model, dataloaders)

2.3 代码结构

超参数设置
数据预处理
LSTM模型定义
权重初始化
训练脚本设置
模型评估
main主函数
2.3.1 超参数设置
# 超参数设置
data_path =  './data/data.txt'              # 数据集
vocab_path = './data/vocab.pkl'             # 词表
save_path = './saved_dict/lstm.ckpt'        # 模型训练结果
embedding_pretrained = \torch.tensor(np.load('./data/embedding_Tencent.npz')["embeddings"].astype('float32'))# 预训练词向量
embed = embedding_pretrained.size(1)        # 词向量维度
dropout = 0.5                               # 随机丢弃
num_classes = 2                             # 类别数
num_epochs = 30                             # epoch数
batch_size = 128                            # mini-batch大小
pad_size = 50                               # 每句话处理成的长度(短填长切)
learning_rate = 1e-3                        # 学习率
hidden_size = 128                           # lstm隐藏层
num_layers = 2                              # lstm层数
MAX_VOCAB_SIZE = 10000                      # 词表长度限制
UNK, PAD = '<UNK>', '<PAD>'                 # 未知字,padding符号
(1)embedding_pretrained = \ torch.tensor(
np.load( './data/embedding_Tencent.npz') ["embeddings"].astype('float32'))

embedding_pretrained是一个预训练的嵌入矩阵,它是通过加载Tencent AI Lab发布的中文词向量进行初始化的。这个嵌入矩阵可以用于自然语言处理任务,如文本分类、命名实体识别等。它的维度是根据预训练模型的设置而定,通常是一个较大的矩阵,每一行代表一个词的向量表示。

加载过程中,首先使用np.load函数加载了一个.npz文件,该文件包含了预训练的词向量。然后通过[“embeddings”]索引获取到具体的词向量矩阵。最后,将该矩阵转换为torch.tensor,并指定数据类型为float32。

 

(2)什么是嵌入矩阵?

 

嵌入矩阵是在自然语言处理(NLP)中常用的一种技术,用于将离散的词或字符转换为连续的向量表示。它是一种将高维离散数据映射到低维连续空间的方法。

在NLP任务中,我们通常使用词嵌入(word embedding)来表示单词。词嵌入是一种将单词映射到实数向量的技术,它可以捕捉到单词之间的语义和语法关系。嵌入矩阵是用来存储这些词嵌入的矩阵,其中每一行对应一个单词的向量表示。

嵌入矩阵的大小通常由两个参数确定:词汇表大小(vocabulary size)和嵌入维度(embedding dimension)。词汇表大小表示有多少个不同的单词需要进行嵌入,而嵌入维度则表示每个单词的向量长度。

通过嵌入矩阵,我们可以将文本中的每个单词转换为对应的向量表示,从而方便进行后续的机器学习或深度学习任务,如文本分类、情感分析等。

(3) 什么是词嵌入?

词嵌入(Word Embedding)是一种将词语映射到实数向量空间的技术,它在自然语言处理领域中被广泛应用。词嵌入的目标是将词语的语义信息编码为向量表示,使得具有相似语义的词在向量空间中距离较近。通过词嵌入,我们可以将文本数据转化为计算机可以理解和处理的形式。

常见的词嵌入模型有Word2Vec、GloVe和FastText等。这些模型通过分析大规模文本语料库中的上下文关系来学习词语的向量表示。具体而言,这些模型会考虑一个词语周围的上下文词语,并尝试预测该词语出现的概率。通过多次迭代训练,模型会学习到每个词语的向量表示,使得具有相似上下文的词在向量空间中距离较近。

词嵌入的应用非常广泛,例如在文本分类、情感分析、机器翻译和信息检索等任务中,可以利用词嵌入来表示文本数据,并进行进一步的分析和处理。

2.3.2数据预处理
(1)函数 get_data()
def get_data():tokenizer = lambda x: [y for y in x]  # 字级别vocab = pkl.load(open(vocab_path, 'rb'))# print('tokenizer',tokenizer)print('vocab',vocab)print(f"Vocab size: {len(vocab)}")train,dev,test = load_dataset(data_path, pad_size, tokenizer, vocab)return vocab, train, dev, test

这段代码是一个函数get_data()的实现。函数的功能是加载数据集并返回相关的数据

首先,函数内部定义了一个tokenizer,它是一个匿名函数,将输入的字符串按字符进行切分。然后,通过pkl.load()函数加载了一个名为vocab_path的文件,得到了一个词汇表vocab。接着,打印了词汇表的大小。

接下来,调用了load_dataset()函数,传入了数据路径data_path、填充大小pad_size、刚刚定义的tokenizer和词汇表vocab作为参数。这个函数的作用是根据给定的数据路径和词汇表,加载训练集、验证集和测试集。

最后,函数返回了词汇表vocab以及加载的训练集、验证集和测试集。

打印输出的内容  

(2)load_dataset函数
def load_dataset(path, pad_size, tokenizer, vocab):'''将路径文本文件分词并转为三元组返回:param path: 文件路径:param pad_size: 每个序列的大小:param tokenizer: 转为字级别:param vocab: 词向量模型:return: 二元组,含有字ID,标签'''contents = []n=0with open(path, 'r', encoding='gbk') as f:# tqdm可以看进度条for line in tqdm(f):# 默认删除字符串line中的空格、’\n’、't’等。lin = line.strip()if not lin:continue# print(lin)label,content = lin.split('	####	')# word_line存储每个字的idwords_line = []# 分割器,分词每个字token = tokenizer(content)# print(token)# 字的长度seq_len = len(token)if pad_size:# 如果字长度小于指定长度,则填充,否则截断if len(token) < pad_size:token.extend([vocab.get(PAD)] * (pad_size - len(token)))else:token = token[:pad_size]seq_len = pad_size# 将每个字映射为ID# 如果在词表vocab中有word这个单词,那么就取出它的id;# 如果没有,就去除UNK(未知词)对应的id,其中UNK表示所有的未知词(out of vocab)都对应该idfor word in token:words_line.append(vocab.get(word, vocab.get(UNK)))n+=1contents.append((words_line, int(label)))train, X_t = train_test_split(contents, test_size=0.4, random_state=42)dev,test= train_test_split(X_t, test_size=0.5, random_state=42)return train,dev,test

 1)contents = [] ;n=0   :定义变量列表contents和int变量n,设初始值为0

2)  with open(path, 'r', encoding='gbk') as f:
        # tqdm可以看进度条
        for line in tqdm(f):
            # 默认删除字符串line中的空格、’\n’、't’等。
            lin = line.strip()
            if not lin:
                continue
            # print(lin)
            label,content = lin.split('    ####    ')
            # word_line存储每个字的id
            words_line = []
            # 分割器,分词每个字
            token = tokenizer(content)
            # print(token)
            # 字的长度
            seq_len = len(token)

使用with open(path, 'r', encoding='gbk') as f:语句打开一个文件,并指定编码为GBK。接下来,使用tqdm库对文件进行迭代,每次读取一行内容。然后,使用strip()方法去除字符串中的空格、换行符等特殊字符。如果字符串为空,则跳过该行。接着,使用split()方法将字符串按照####进行分割,得到标签和内容

3)  if pad_size:
                # 如果字长度小于指定长度,则填充,否则截断
                if len(token) < pad_size:
                    token.extend([vocab.get(PAD)] * (pad_size - len(token)))
                else:
                    token = token[:pad_size]
                    seq_len = pad_size
            # 将每个字映射为ID
            # 如果在词表vocab中有word这个单词,那么就取出它的id;
            # 如果没有,就去除UNK(未知词)对应的id,其中UNK表示所有的未知词(out of vocab)都对应该id
            for word in token:
                words_line.append(vocab.get(word, vocab.get(UNK)))
            n+=1
            contents.append((words_line, int(label)))

对内容进行分词处理,将每个字转换为对应的id,并存储在words_line列表中。如果指定了pad_size(填充大小),则根据指定大小进行填充或截断处理。最后,将处理后的内容和标签添加到contents列表中。

4)train, X_t = train_test_split(contents, test_size=0.4, random_state=42) dev,test= train_test_split(X_t, test_size=0.5, random_state=42)

train_test_split是一个用于将数据集划分为训练集和测试集的函数。它的作用是将给定的数据集按照指定的比例进行划分,并返回划分后的训练集和测试集

在你提供的代码中,train_test_split函数被用来将contents数据集划分为三个部分:train、dev和test。具体的划分方式如下:

首先,使用train_test_split函数将contents数据集按照test_size参数指定的比例(这里是0.4)进行划分,其中random_state参数用于设置随机种子,保证每次划分结果的一致性。划分后,得到的训练集和测试集被赋值给变量X_t。

接着,再次使用train_test_split函数将X_t数据集按照test_size参数指定的比例(这里是0.5)进行划分,同样使用random_state参数设置随机种子。划分后,得到的训练集和测试集被赋值给变量dev和test。

总结一下,train_test_split函数的作用是将给定的数据集按照指定的比例进行划分,并返回划分后的训练集和测试集。在你提供的代码中,通过两次调用train_test_split函数,将contents数据集划分为了train、dev和test三个部分。

(3)自定义类 TextDataset
class TextDataset(Dataset):def __init__(self, data):self.device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')self.x = torch.LongTensor([x[0] for x in data]).to(self.device)self.y = torch.LongTensor([x[1] for x in data]).to(self.device)def __getitem__(self,index):self.text = self.x[index]self.label = self.y[index]return self.text, self.labeldef __len__(self):return len(self.x)

 

class TextDataset是一个自定义的数据集类,继承自torch.utils.data.Dataset。它用于加载文本数据,并将其转换为模型可以处理的格式。

在初始化方法__init__中,它接受一个参数data,该参数是一个包含文本和标签的列表。首先,它通过torch.cuda.is_available()判断是否有可用的GPU,如果有,则将self.device设置为torch.device('cuda'),否则设置为torch.device('cpu')。接下来,它将文本和标签分别存储在self.x和self.y中,并将它们转换为torch.LongTensor类型,并将其移动到self.device上。

在__getitem__方法中,它接受一个索引index,并从self.x和self.y中获取对应索引的文本和标签。然后,将文本和标签作为元组返回。

在__len__方法中,它返回self.x的长度,即数据集的样本数量。

 2.3.3 LSTM模型定义
class Model(nn.Module):def __init__(self):super(Model, self).__init__()# 使用预训练的词向量模型,freeze=False 表示允许参数在训练中更新# 在NLP任务中,当我们搭建网络时,第一层往往是嵌入层,对于嵌入层有两种方式初始化embedding向量,# 一种是直接随机初始化,另一种是使用预训练好的词向量初始化。self.embedding = nn.Embedding.from_pretrained(embedding_pretrained, freeze=False)# bidirectional=True表示使用的是双向LSTMself.lstm = nn.LSTM(embed, hidden_size, num_layers,bidirectional=True, batch_first=True, dropout=dropout)# 因为是双向LSTM,所以层数为config.hidden_size * 2self.fc = nn.Linear(hidden_size * 2, num_classes)def forward(self, x):out = self.embedding(x)# lstm 的input为[batchsize, max_length, embedding_size],输出表示为 output,(h_n,c_n),# 保存了每个时间步的输出,如果想要获取最后一个时间步的输出,则可以这么获取:output_last = output[:,-1,:]out, _ = self.lstm(out)out = self.fc(out[:, -1, :])  # 句子最后时刻的 hidden statereturn out

这段代码是一个PyTorch中的神经网络模型类,继承自nn.Module。该模型包含了一个嵌入层(embedding)、一个双向LSTM层(lstm)和一个全连接层(fc)。

在初始化方法__init__中,首先调用了父类nn.Module的初始化方法,然后定义了模型的各个层。其中,self.embedding是一个嵌入层,使用预训练的词向量模型进行初始化,并设置freeze=False表示允许参数在训练中更新。self.lstm是一个双向LSTM层,输入为嵌入层的输出,输出为LSTM的输出和最后一个时间步的隐藏状态。self.fc是一个全连接层,将LSTM的输出映射到最终的类别数。

在前向传播方法forward中,首先将输入x通过嵌入层进行词嵌入,然后将词嵌入结果输入到LSTM层中得到输出。最后,将LSTM的输出的最后一个时间步的隐藏状态通过全连接层映射到最终的类别数,并返回结果。

 

(1)nn.Moudel

 

nn.Module是PyTorch中的一个基类,用于构建神经网络模型。它提供了一些常用的方法和属性,方便我们定义和管理神经网络的结构。

nn.Module的主要作用是将神经网络的各个层组织起来,并提供了一些方法来管理这些层。通过继承nn.Module类,我们可以定义自己的神经网络模型,并在其中定义各个层的结构和操作。

在nn.Module中,我们可以使用add_module()方法来添加子模块,使用forward()方法来定义前向传播的逻辑。nn.Module还提供了一些其他常用的方法,如parameters()用于获取模型的可学习参数,zero_grad()用于将参数的梯度置零等。

使用nn.Module构建神经网络模型的好处是可以方便地管理和调用各个层,同时也可以利用PyTorch提供的自动求导功能进行反向传播和参数更新。

(2)super(Model, self).__init__() 

super(Model, self).init()是Python中用于调用父类构造函数的一种方式。它在子类的构造函数中使用,以便在子类中可以继承并初始化父类的属性和方法。

具体来说,super()函数返回一个临时对象,该对象绑定了父类的方法。通过调用该对象的方法,可以在子类中调用父类的方法。在调用父类构造函数时,通常使用super(Model, self).init()来确保父类的构造函数被正确地调用。

这种方式的好处是可以避免在子类中重复编写父类的构造函数代码,同时确保父类的构造函数被正确地执行。这对于多层继承的情况尤为重要,因为使用super()可以按照方法解析顺序(MRO)调用所有父类的构造函数。

(3)def forward(self, x): 

在Python中,def forward(self, x):是一个函数定义的语法。通常,这是在类中定义的一个方法,用于执行某些操作或计算。

在深度学习中,forward方法通常用于定义神经网络模型的前向传播过程。在这个方法中,输入x会经过一系列的网络层和激活函数,最终得到输出结果。

具体来说,forward方法接收一个输入x作为参数,并返回一个输出结果。在这个方法中,你可以使用各种神经网络层(如全连接层、卷积层等)和激活函数(如ReLU、Sigmoid等)来对输入进行处理和转换。

2.3.4 权重初始化
def init_network(model, method='xavier', exclude='embedding'):for name, w in model.named_parameters():if exclude not in name:if 'weight' in name:if method == 'xavier':nn.init.xavier_normal_(w)elif method == 'kaiming':nn.init.kaiming_normal_(w)else:nn.init.normal_(w)elif 'bias' in name:nn.init.constant_(w, 0)else:pass

这段代码是一个初始化神经网络权重和偏置的函数。它接受一个模型对象和一些可选参数,其中method参数用于指定初始化方法,默认为’xavier’,exclude参数用于指定不需要初始化的部分,默认为’embedding’。

函数通过遍历模型的参数来进行初始化。对于权重参数,如果参数名中不包含exclude字符串,则根据method参数选择相应的初始化方法,分别为xavier初始化、kaiming初始化和普通初始化。对于偏置参数,使用常数0进行初始化。

如果参数名既不包含’weight’也不包含’bias’,则不进行任何操作。

 


2.3.5 训练脚本设置
def train( model, dataloaders):'''训练模型:param model: 模型:param dataloaders: 处理后的数据,包含trian,dev,test'''optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)loss_function = torch.nn.CrossEntropyLoss()dev_best_loss = float('inf')device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')print("Start Training...\n")plot_train_acc = []plot_train_loss = []for i in range(num_epochs):# 1,训练循环----------------------------------------------------------------# 将数据全部取完# 记录每一个batchstep = 0train_lossi=0train_acci = 0for inputs, labels in dataloaders['train']:# 训练模式,可以更新参数model.train()# print(inputs.shape)inputs = inputs.to(device)labels = labels.to(device)# 梯度清零,防止累加optimizer.zero_grad()outputs = model(inputs)loss = loss_function(outputs, labels)loss.backward()optimizer.step()step += 1true = labels.data.cpu()predic = torch.max(outputs.data, 1)[1].cpu()train_lossi += loss.item()train_acci += metrics.accuracy_score(true, predic)# 2,验证集验证----------------------------------------------------------------dev_acc, dev_loss = dev_eval(model, dataloaders['dev'], loss_function,Result_test=False)if dev_loss < dev_best_loss:dev_best_loss = dev_losstorch.save(model.state_dict(), save_path)train_acc = train_acci/steptrain_loss = train_lossi/stepplot_train_acc.append(train_acc)plot_train_loss.append(train_loss)print("epoch = {} :  train_loss = {:.3f}, train_acc = {:.2%}, dev_loss = {:.3f}, dev_acc = {:.2%}".format(i+1, train_loss, train_acc, dev_loss, dev_acc))plot_acc(plot_train_acc)plot_loss(plot_train_loss)# 3,验证循环----------------------------------------------------------------model.load_state_dict(torch.load(save_path))model.eval()start_time = time.time()test_acc, test_loss = dev_eval(model, dataloaders['test'], loss_function,Result_test=True)print('================'*8)print('test_loss: {:.3f}      test_acc: {:.2%}'.format(test_loss, test_acc))

(1)优化器设置,参数定义

  1. optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate):这行代码定义了优化器,使用Adam优化算法来更新模型的参数。model.parameters()表示需要优化的参数,lr=learning_rate表示学习率。

  2. loss_function = torch.nn.CrossEntropyLoss():这行代码定义了损失函数,使用交叉熵损失函数来计算模型的预测值与真实值之间的差异。

  3. dev_best_loss = float('inf'):这行代码初始化了一个变量dev_best_loss,用于保存最佳的验证集损失值。初始值设置为正无穷大。

  4. device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu'):这行代码根据是否有可用的GPU来选择设备,如果有GPU则使用cuda,否则使用cpu。

  5. print("Start Training...\n"):这行代码打印出开始训练的提示信息。

  6. plot_train_acc = []:这行代码初始化了一个空列表plot_train_acc,用于保存训练过程中的准确率。

  7. plot_train_loss = []:这行代码初始化了一个空列表plot_train_loss,用于保存训练过程中的损失值。

(2)模型训练

这段代码是一个训练循环,用于对模型进行训练。下面是对代码的解释:

1. `for i in range(num_epochs):`:这是一个循环,用于指定训练的轮数,`num_epochs`表示总共的训练轮数。

2. `step = 0`:初始化步数,用于记录每一个batch的训练进度。

3. `train_lossi=0`和`train_acci = 0`:初始化训练损失和训练准确率,用于记录每一个batch的训练结果。

4. `for inputs, labels in dataloaders['train']:`:这是一个数据迭代器,用于遍历训练数据集。`inputs`表示输入数据,`labels`表示对应的标签。

5. `model.train()`:将模型设置为训练模式,这样可以更新模型的参数。

6. `inputs = inputs.to(device)`和`labels = labels.to(device)`:将输入数据和标签移动到指定的设备上进行计算,比如GPU。

7. `optimizer.zero_grad()`:梯度清零,防止梯度累加。

8. `outputs = model(inputs)`:将输入数据输入到模型中进行前向传播,得到模型的输出。

9. `loss = loss_function(outputs, labels)`:计算模型输出与真实标签之间的损失。

10. `loss.backward()`:反向传播,计算梯度。

11. `optimizer.step()`:更新模型的参数。

12. `step += 1`:更新步数。

13. `true = labels.data.cpu()`和`predic = torch.max(outputs.data, 1).cpu()`:将标签和预测结果移动到CPU上,并转换为numpy数组。

14. `train_lossi += loss.item()`和`train_acci += metrics.accuracy_score(true, predic)`:累加训练损失和训练准确率。

 (3)验证集验证

     dev_acc, dev_loss = dev_eval(model, dataloaders['dev'], loss_function,Result_test=False)
        if dev_loss < dev_best_loss:
            dev_best_loss = dev_loss
            torch.save(model.state_dict(), save_path)
        train_acc = train_acci/step
        train_loss = train_lossi/step
        plot_train_acc.append(train_acc)
        plot_train_loss.append(train_loss)
        print("epoch = {} :  train_loss = {:.3f}, train_acc = {:.2%}, dev_loss = {:.3f}, dev_acc = {:.2%}".
                  format(i+1, train_loss, train_acc, dev_loss, dev_acc))
    plot_acc(plot_train_acc)
    plot_loss(plot_train_loss)

这段代码是一个训练过程中的评估部分。首先,使用dev_eval函数对模型在验证集上进行评估,得到dev_accdev_loss。然后,通过比较当前的dev_loss与之前的最佳dev_loss,如果当前的dev_loss更小,则更新最佳dev_loss并保存模型参数到save_path。接下来,计算训练集上的准确率和损失,并将其存储到train_acctrain_loss中。最后,打印出当前的训练损失、训练准确率、验证损失和验证准确率。 

(4) 测试集测试

model.load_state_dict(torch.load(save_path)) model.eval() start_time = time.time() test_acc, test_loss = dev_eval(model, dataloaders['test'], loss_function,Result_test=True) print('================'*8) print('test_loss: {:.3f} test_acc: {:.2%}'.format(test_loss, test_acc))

model.load_state_dict(torch.load(save_path))是一个用于加载模型参数的函数。它将保存在save_path路径下的模型参数加载到model中。这个函数通常在训练过程中使用,当我们需要保存和加载模型的参数时,可以使用这个函数。

model.eval()是一个用于将模型设置为评估模式的函数。在评估模式下,模型会关闭一些具有随机性的操作,例如dropout和batch normalization。这样可以确保在测试集上的结果是稳定和可重复的。

start_time = time.time()是用于记录代码执行开始时间的语句。

test_acc, test_loss = dev_eval(model, dataloaders['test'], loss_function, Result_test=True)是调用了一个名为dev_eval的函数来对模型在测试集上进行评估。它接受四个参数:model表示要评估的模型,dataloaders['test']表示测试集的数据加载器,loss_function表示损失函数,Result_test=True表示要返回测试集上的结果。

最后两行代码用于打印测试集上的损失和准确率。


2.3.6 模型评估
def dev_eval(model, data, loss_function,Result_test=False):''':param model: 模型:param data: 验证集集或者测试集的数据:param loss_function: 损失函数:return: 损失和准确率'''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:outputs = model(texts)loss = loss_function(outputs, labels)loss_total += loss.item()labels = labels.data.cpu().numpy()predic = torch.max(outputs.data, 1)[1].cpu().numpy()labels_all = np.append(labels_all, labels)predict_all = np.append(predict_all, predic)acc = metrics.accuracy_score(labels_all, predict_all)if Result_test:result_test(labels_all, predict_all)else:passreturn acc, loss_total / len(data)

这是一个用于模型评估的函数。它接受一个模型、数据集、损失函数作为输入,并返回损失和准确率。

函数首先将模型设置为评估模式(model.eval()),然后初始化损失总和(loss_total)和空的预测结果数组(predict_all)和标签数组(labels_all)。

接下来,使用torch.no_grad()上下文管理器,对数据集中的每个样本进行循环。对于每个样本,将输入数据传递给模型并计算输出。然后使用损失函数计算损失,并将其累加到损失总和中。同时,将标签转换为numpy数组,并将模型的预测结果转换为numpy数组。将标签和预测结果分别添加到labels_all和predict_all数组中。

最后,使用metrics.accuracy_score计算准确率,并根据Result_test参数决定是否调用result_test函数进行结果测试。

 


2.3.7 main主函数
if __name__ == '__main__':# 设置随机数种子,保证每次运行结果一致,不至于不能复现模型np.random.seed(1)torch.manual_seed(1)torch.cuda.manual_seed_all(1)torch.backends.cudnn.deterministic = True  # 保证每次结果一样start_time = time.time()print("Loading data...")vocab, train_data, dev_data, test_data = get_data()dataloaders = {'train': DataLoader(TextDataset(train_data), batch_size, shuffle=True),'dev': DataLoader(TextDataset(dev_data), batch_size, shuffle=True),'test': DataLoader(TextDataset(test_data), batch_size, shuffle=True)}time_dif = get_time_dif(start_time)print("Time usage:", time_dif)device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')model = Model().to(device)init_network(model)train(model, dataloaders)

(1)if name == ‘main

if name == ‘main’ 是Python中的一个常用语句,它用于判断当前模块是否是直接被运行的,而不是被导入的。当一个Python文件被直接运行时,其__name__属性的值为’main’,而当它被导入时,__name__属性的值为模块名。

(2)设置随机种子 

    # 设置随机数种子,保证每次运行结果一致,不至于不能复现模型
    np.random.seed(1)
    torch.manual_seed(1)
    torch.cuda.manual_seed_all(1)
    torch.backends.cudnn.deterministic = True  # 保证每次结果一样

(3)加载数据

    start_time = time.time()
    print("Loading data...")
    vocab, train_data, dev_data, test_data = get_data()
    dataloaders = {
        'train': DataLoader(TextDataset(train_data), batch_size, shuffle=True),
        'dev': DataLoader(TextDataset(dev_data), batch_size, shuffle=True),
        'test': DataLoader(TextDataset(test_data), batch_size, shuffle=True)
    }
    time_dif = get_time_dif(start_time)
    print("Time usage:", time_dif)

这段代码是一个简单的数据加载和处理的过程。首先,通过调用get_data()函数获取数据集的词汇表(vocab)以及训练集(train_data)、验证集(dev_data)和测试集(test_data)。然后,使用DataLoader类将数据集封装成可迭代的数据加载器,其中训练集、验证集和测试集分别对应不同的数据加载器。最后,通过调用get_time_dif()函数计算代码执行的时间,并将结果打印出来。

(4)调用train函数进行训练

    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
    model = Model().to(device)
    init_network(model)
    train(model, dataloaders)

  1. device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') 这行代码的作用是检查是否有可用的GPU,如果有,则将设备设置为CUDA,否则设置为CPU。这样可以利用GPU的并行计算能力来加速模型训练。

  2. model = Model().to(device) 这行代码创建了一个模型对象,并将其移动到之前设置好的设备上。这样可以确保模型在正确的设备上进行计算。

  3. init_network(model) 这行代码是一个自定义函数,用于初始化模型的权重和其他参数。这个函数可能会根据具体的需求进行不同的操作,比如随机初始化权重、加载预训练的权重等。

  4. train(model, dataloaders) 这行代码是一个自定义函数,用于训练模型。它接受一个模型对象和一个数据加载器作为输入,并在训练过程中对模型进行更新。具体的训练过程可能包括前向传播、计算损失、反向传播、优化器更新等步骤。

最终结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/288329.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sentry(Android)源码解析

本文字数&#xff1a;16030字 预计阅读时间&#xff1a;40分钟 01 前言 Sentry是一个日志记录、错误上报、性能监控的开源框架&#xff0c;支持众多平台&#xff1a; 其使用方式在本文不进行说明了&#xff0c;大家可参照官方文档&#xff1a;https://docs.sentry.io/platforms…

Jenkins--在Linux上使用Docker安装

一、Jenkins 简介 Jenkins是一个流行的开源自动化服务器&#xff0c;用于持续集成和持续交付&#xff08;CI/CD&#xff09;。Jenkins的核心功能主要包括以下几点&#xff1a; 持续集成&#xff1a;Jenkins可以监控版本控制系统&#xff08;如Git、SVN&#xff09;中的代码变…

RAG进阶笔记:RAG进阶

1 查询/索引部分 1.1 层次索引 创建两个索引——一个由摘要组成&#xff0c;另一个由文档块组成分两步进行搜索&#xff1a;首先通过摘要过滤出相关文档&#xff0c;接着只在这个相关群体内进行搜索 1.2 假设性问题 让LLM为每个块生成一个假设性问题&#xff0c;并将这些问…

U盘未格式化,数据恢复攻略大揭秘

U盘遭遇未格式化困境&#xff0c;数据安全岌岌可危 在日常的工作和生活中&#xff0c;U盘以其便携、容量大的特性成为了我们不可或缺的存储工具。然而&#xff0c;有时我们会遇到这样一个棘手的问题&#xff1a;当我们将U盘插入电脑时&#xff0c;却收到了“未格式化”的提示。…

畅捷通T+ Ufida.T.DI.UIP.RRA.RRATableController 远程命令执行漏洞

一、漏洞信息 漏洞名称&#xff1a;畅捷通T Ufida.T.DI.UIP.RRA.RRATableController 远程命令执行漏洞 漏洞类别&#xff1a;远程命令执行漏洞 风险等级&#xff1a;高危 二、漏洞描述 畅捷通TPlus适用于异地多组织、多机构对企业财务汇总的管理需求&#xff1b;全面支持企…

为什么 Hashtable 不允许插入 null 键 和 null 值?

1、典型回答 浅层次的来回答这个问题的答案是&#xff0c;JDK 源码不支持 Hashtable 插入 value 值为 null&#xff0c;如以下JDK 源码所示&#xff1a; 也就是JDK 源码规定了&#xff0c;如果你给 Hashtable 插入 value 值为 null 就会抛出空指针异常 并目看上面的JDK 源码可…

js算法记录

> 更多请前往 https://www.passerma.com/article/86 滑动窗口 1 给定一个矩阵&#xff0c;包含N*M个整数&#xff0c;和一个包含K个整数的数组。现在要求在这个矩阵中找一个宽度最小的子矩阵&#xff0c;要求子矩阵包含数组中所有的整数 function minSubmatrixWidth(mat…

PCL拟合并绘制平面(二)

使用RANSAC拟合点云平面 1、C实现2、效果图 普通的点云平面拟合方式在一般情况下可以得到较好的平面拟合效果&#xff0c;但是容易出现平面拟合错误或是拟合的平面不是最优的情况。此时就需要根据自己的实际使用情况&#xff0c;调整平面拟合的迭代次数以及收敛条件。 使用RAN…

vue脚手架创建项目:账号登录(利用element-ui快速开发)(取消eslint强制格式)(修改端口号)

新手看不懂&#xff0c;老手不用看系列 文章目录 一、准备工作1.1 取消强制格式检查1.2 导入依赖&#xff0c;注册依赖 二、去element-ui官网找样式写Login组件2.1 引用局部组件2.2 运行项目 三、看一下发现没问题&#xff0c;开始修改前端的代码四、修改端口号4.1 修改后端端口…

一款比 K8S 更好用的编排工具——Nomod 单机部署

上下文 最近公司需要调研类似 EMCHub 这样支持算力共享的服务。第一直觉是使用 K8S 或 K3S&#xff0c;作为 CNCF 孵化的顶级项目&#xff0c;同时也是当前云原生生态使用最广的编排系统。但是在学习 EMC Hub 源码过程中&#xff0c;偶然发现它是基于 Nomad 做的集群管理。 相…

Python学习笔记------文件操作

编码 编码就是一种规则集合&#xff0c;记录了内容和二进制间进行相互转换的逻辑。 编码有许多中&#xff0c;我们最常用的是UTF-8编码 计算机只认识0和1&#xff0c;所以需要将内容翻译成0和1才能保存在计算机中。同时也需要编码&#xff0c;将计算机保存的0和1&#xff0c…

maya导入导出bvh 自动 脚本

目录 maya打开脚本编辑器 运行打开bvh脚本 maya导出bvh脚本 maya打开脚本编辑器 打开Maya软件,点击右下角 “脚本编辑器” 运行打开bvh脚本<

Spring:面试八股

文章目录 参考Spring模块CoreContainerAOP 参考 JavaGuide Spring模块 CoreContainer Spring框架的核心模块&#xff0c;主要提供IoC依赖注入功能的支持。内含四个子模块&#xff1a; Core&#xff1a;基本的核心工具类。Beans&#xff1a;提供对bean的创建、配置、管理功能…

国内ip地址推荐,畅享网络新体验!

在数字化时代&#xff0c;IP地址不仅是网络连接的基石&#xff0c;也是互联网产业发展的重要标志。国内作为全球互联网市场的重要参与者&#xff0c;拥有众多IP地址资源。虎观代理小二旨在探索并推荐一些国内IP地址&#xff0c;分析它们的价值所在&#xff0c;并探讨如何更好地…

华为数通 HCIP-Datacom H12-831 题库补充(3/27)

2024年 HCIP-Datacom&#xff08;H12-831&#xff09;最新题库&#xff0c;完整题库请扫描上方二维码&#xff0c;持续更新。 如图所示&#xff0c;关于R4路由器通过IS-IS计算出来的IPv6路由&#xff0c;哪一选项的描述是错误的&#xff1f; A&#xff1a;R4通过IS—IS只学习到…

stm32f103c8t6学习笔记(学习B站up江科大自化协)-DMA

DMA简介 DMA主要用于协助CPU完成数据转运的工作 DMA&#xff0c;英文全称Direct Memory Access&#xff0c;DMA这个外设是可以直接访问STM32内部存储器的&#xff0c;包括运行内存SRAM&#xff0c;程序存储器flash和寄存器等等&#xff0c;DMA都有权限访问&#xff0c;所以DMA能…

PHP页面如何实现设置独立访问密码

PHP网页如果需要查看信息必须输入密码&#xff0c;验证后才可显示出内容的代码如何实现&#xff1f; 对某些php页面设置单独的访问密码,如果密码不正确则无法查看内容,相当于对页面进行了一个加密。 如何实现这个效果&#xff0c;详细教程可以参考&#xff1a;PHP页面如何实现…

基于react native的自定义轮播图

基于react native的自定义轮播图 效果示例图示例代码 效果示例图 示例代码 import React, {useEffect, useRef, useState} from react; import {Animated,PanResponder,StyleSheet,Text,View,Dimensions, } from react-native; import {pxToPd} from ../../common/js/device;c…

【Linux】进程状态(R运行状态、S睡眠状态、D磁盘休眠状态、T停止状态、X死亡状态)

目录 01.运行状态 02.睡眠状态 03.磁盘睡眠状态 04.停止状态 05.死亡状态 进程的状态会随着操作系统的调度和外部事件的发生而不断地发生转换。例如&#xff0c;一个新创建的进程经过初始化后会进入就绪态&#xff0c;等待被调度执行&#xff1b;当调度器分配处理器资源给…

Windows直接运行python程序

Windows直接运行python程序 一、新建bat脚本二、新建vbs脚本 一、新建bat脚本 新建bat批处理脚本&#xff0c;写入以下内容 echo off call conda activate pytorch python app.pyecho off&#xff1a;在此语句后所有运行的命令都不显示命令行本身&#xff0c;但是本身的指令是…