深度学习处理文本

一、反向传播

二、优化器

1. Adam优化器

1.1.Adam优化器相关参数

:学习率,一般取0.001

:一阶矩估计指数衰减率,一般取0.9

:二阶矩估计指数衰减率,一般取0.999

:微小常数(如 )防止除以零

:带有参数的随机目标函数,是模型训练中需要最小化(或最大化)的函数

:初始参数向量,模型参数的初始值

1.2.Adam优化器算法步骤

1.初始化

  • :初始化一阶矩向量为0。
  • :初始化二阶矩向量为0。
  • :初始化时间步为0。

2.迭代过程

2.1.更新一阶矩和二阶矩估计

2.2.偏差修正 (Bias Correction)

2.3.参数更新

含义 :Adam的更新规则可以直观理解为:用带有动量的梯度作为更新方向,同时用类似RMSProp的来对每个参数的学习率进行自适应缩放。偏差修正确保了在训练初期,矩估计也是无偏的。

1.3. Adam优化器优势及适用场景

  1. 实现简单,计算高效,对内存需求少

  2. 超参数具有很好的解释性,且通常无需调整或仅需很少的微调

  3. 更新的步长能够被限制在大致的范围内(初始学习率)

  4. 能够表现出自动调整学习率

  5. 很适合应用于大规模的数据及参数的场景

  6. 适用于不稳定目标函数

  7. 适用于梯度稀疏或梯度存在很大噪声的问题

2. AdamW优化器

在标准的L2正则化中,损失函数会增加一项,其梯度为。在Adam中,这个正则项梯度会进入的计算,使得权重衰减的效果与梯度的量级耦合在一起,导致对大学习率和较大权重的参数,实际的权重衰减效果会减弱。

AdamW 将权重衰减从梯度计算中解耦出来,直接在参数更新步骤中应用:

1.执行标准的Adam更新 (不含正则项的梯度)

2.独立进行权重衰减:

含义:权重衰减不再影响梯度的移动平均估计。它变成了一个独立的、对所有参数(通常除了Normalization层和Bias)施加的、与其梯度大小无关的"缩放"步骤,更符合其作为正则化手段的初衷。

三、NLP任务

任务:字符串分类:判断字符串中是否出现了指定字符

指定字符:a 正样本:abcd 负样本:bcdb

Step1:字符数值化:每个字符转化为同维度向量

"abcd" -> 4*5矩阵 矩阵形状 = 文本长度 * 向量长度

通过embedding层实现

1.embedding层

embedding矩阵是可训练的参数,一般会在模型构建时随机初始化,也可以使用预训练的词向量来做初始化,此时也可以选择不训练embedding层中的参数

输入的整数序列可以有重复,但取值不能超过Embedding矩阵的列数

核心价值:将离散值转化为向量,在nlp任务和各类特征工程中应用广泛

搭配词表文件

对于中文通常使用

对于英文使用token

多个语种和符号可以出现在同一份词表中

目的:"abc" --词表--> 0,1,2 --Embedding层--> 3*n的矩阵 --> model

python 复制代码
import torch
import torch.nn as nn

'''
embedding层的处理
'''

num_embeddings = 6  #通常对于nlp任务,此参数为字符集字符总数
embedding_dim = 5   #每个字符向量化后的向量维度
embedding_layer = nn.Embedding(num_embeddings, embedding_dim)
print("随机初始化权重")
print(embedding_layer.weight)
print("################")

#构造字符表
vocab = {
    "a" : 0,
    "b" : 1,
    "c" : 2,
    "d" : 3,
    "e" : 4,
    "f" : 5,
}

def str_to_sequence(string, vocab):
    return [vocab[s] for s in string]

string1 = "abcde"
string2 = "ddccb"
string3 = "fedab"

sequence1 = str_to_sequence(string1, vocab)
sequence2 = str_to_sequence(string2, vocab)
sequence3 = str_to_sequence(string3, vocab)

print(sequence1)
print(sequence2)
print(sequence3)

x = torch.LongTensor([sequence1, sequence2, sequence3])
embedding_out = embedding_layer(x)
print(embedding_out)

Step2:矩阵转化为向量

池化(求平均)

2.池化层

  • 降低了后续网络层的输入维度,缩减模型大小,提高计算速度
  • 提高了特征的鲁棒性,防止过拟合

Step3:向量到数值

采取最简单的线性公式

Step4:数值归一化

sigmoid函数进行归一化处理,映射到01之间

注意:字母向量和线性层的w和b需要通过训练优化

3.NLP任务Demo

python 复制代码
import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt

"""

基于pytorch的网络编写
实现一个网络完成一个简单nlp任务
判断文本中是否有某些特定字符出现

"""

class TorchModel(nn.Module):
    def __init__(self, vector_dim, sentence_length, vocab):
        super(TorchModel, self).__init__()
        self.embedding = nn.Embedding(len(vocab), vector_dim)  #embedding层
        self.pool = nn.AvgPool1d(sentence_length)   #池化层
        self.classify = nn.Linear(vector_dim, 1)     #线性层
        self.activation = torch.sigmoid     #sigmoid归一化函数
        self.loss = nn.functional.mse_loss  #loss函数采用均方差损失

    #当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.embedding(x)                      #(batch_size, sen_len) -> (batch_size, sen_len, vector_dim)
        x = x.transpose(1, 2)                      #(batch_size, sen_len, vector_dim) -> (batch_size, vector_dim, sen_len)
        x = self.pool(x)                           #(batch_size, vector_dim, sen_len)->(batch_size, vector_dim, 1)
        x = x.squeeze()                            #(batch_size, vector_dim, 1) -> (batch_size, vector_dim)
        x = self.classify(x)                       #(batch_size, vector_dim) -> (batch_size, 1) 3*5 5*1 -> 3*1
        y_pred = self.activation(x)                #(batch_size, 1) -> (batch_size, 1)
        if y is not None:
            return self.loss(y_pred, y)   #预测值和真实值计算损失
        else:
            return y_pred                 #输出预测结果

#字符集随便挑了一些字,实际上还可以扩充
#为每个字生成一个标号
#{"a":1, "b":2, "c":3...}
#abc -> [1,2,3]
def build_vocab():
    chars = "abcdefghijklmnopqrstuvwxyz"  #字符集
    vocab = {"pad":0}
    for index, char in enumerate(chars):
        vocab[char] = index+1   #每个字对应一个序号
    vocab['unk'] = len(vocab) #26
    return vocab

#随机生成一个样本
#从所有字中选取sentence_length个字
#反之为负样本
def build_sample(vocab, sentence_length):
    #随机从字表选取sentence_length个字,可能重复
    x = [random.choice(list(vocab.keys())) for _ in range(sentence_length)]
    #指定哪些字出现时为正样本
    if set("abc") & set(x):
        y = 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.FloatTensor(dataset_y)

#建立模型
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个用于测试的样本
    print("本次预测集中共有%d个正样本,%d个负样本"%(sum(y), 200 - sum(y)))
    correct, wrong = 0, 0
    with torch.no_grad():
        y_pred = model(x)      #模型预测
        for y_p, y_t in zip(y_pred, y):  #与真实标签进行对比
            if float(y_p) < 0.5 and int(y_t) == 0:
                correct += 1   #负样本判断正确
            elif float(y_p) >= 0.5 and int(y_t) == 1:
                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 = 6   #样本文本长度
    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

#使用训练好的模型做预测
def predict(model_path, vocab_path, input_strings):
    char_dim = 20  # 每个字的维度
    sentence_length = 6  # 样本文本长度
    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(input_strings):
        print("输入:%s, 预测类别:%d, 概率值:%f" % (input_string, round(float(result[i])), result[i])) #打印结果



if __name__ == "__main__":
    main()
    test_strings = ["fnvfee", "wzsdfg", "rqwdeg", "nakwww"]
    predict("model.pth", "vocab.json", test_strings)

四、网络结构

1.全连接层

2.RNN(Recurrent Neural Network,循环神经网络)

主要思想:将整个序列划分成多个时间步,将每一个时间步的信息依次输入模型,同时将模型输出的结果传给下一个时间步

公式:

python 复制代码
class TorchRNN(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(TorchRNN, self).__init__()
        self.layer = nn.RNN(input_size, hidden_size, bias=False, batch_first=True)

    def forward(self, x):
        return self.layer(x)

3.CNN(Convolutional Neural Network,卷积神经网络)

以卷积操作为基础的网络结构,每个卷积核可以看成一个特征提取器

python 复制代码
class TorchCNN(nn.Module):
    def __init__(self, in_channel, out_channel, kernel):
        super(TorchCNN, self).__init__()
        self.layer = nn.Conv2d(in_channel, out_channel, kernel, bias=False)

    def forward(self, x):
        return self.layer(x)

4.归一化 Normalization

batch normalization layer normalization

5.Dropout层

作用:减少过拟合

按照指定概率,随机丢弃一些神经元(将其化为零)

其余元素乘以 1 / (1 -- p)进行放大

如何理解其作用

1)强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,消除减弱了神经元节点间的联合适应性,增强了泛化能力

2)可以看做是一种模型平均,由于每次随机忽略的隐层节点都不同,这样就使每次训练的网络都是不一样的,每次训练都可以单做一个"新"的模型

启示:计算方式并不是越复杂就越好

相关推荐
漂流瓶jz2 小时前
【AI】大模型本地部署与量化:Ollama、transformers、llama.cpp实践
人工智能·llm·ai编程
飞哥数智坊3 小时前
AI时代,我们到底该学什么、用什么,为什么还没提效?
人工智能
飞哥数智坊3 小时前
GPT 做架构,国内模型写代码:这条 AI 开发路线能跑通吗?
人工智能·ai编程
AI_AGENT_DEV_AI4 小时前
AI 智能体的开发与上线
人工智能
VL——MOESR4 小时前
【具身智能】VLA论文阅读随笔
论文阅读·人工智能·机器学习·具身智能·vla
OCR_133716212754 小时前
从模板匹配到AI泛化识别:文本抽取如何重构护照阅读器落地生态
人工智能·重构
大唐荣华4 小时前
从OpenAI关停Sora看世界模型、AI视频与国内具身智能赛道路线分化
人工智能·openai·sora·内容生成
DevOps老兵4 小时前
AI Infra实战02:GPU监控实战,用DCGM+Prometheus+Grafana看清每一张卡
人工智能·grafana·prometheus·ai infra·gpu监控·dcgm
旧日之血_Hayter4 小时前
Antigravity 工作流的实践记录
人工智能
dozenyaoyida4 小时前
AI与大模型新闻日报 | 2026-09-01
人工智能·ai·大模型·新闻