1.训练一个线性投影层
import torch
import torch.nn as nn
import torch.optim as optim
# 定义全连接层
fc = nn.Linear(10, 5) # 输入大小为10,输出大小为5
# 定义损失函数
criterion = nn.MSELoss()
# 定义优化器
optimizer = optim.SGD(fc.parameters(), lr=0.01)
# 循环进行训练
for epoch in range(num_epochs):
# 前向传播
output = fc(input_data)
# 计算损失
loss = criterion(output, target_data)
# 反向传播
optimizer.zero_grad()
loss.backward()
# 更新参数
optimizer.step()
2 nn.module类
nn.Module
主要有以下用途:
-
定义神经网络的层:
nn.Module
包含了很多常用的神经网络层,如全连接层、卷积层、池化层等。我们可以通过继承nn.Module
来自定义新的神经网络层,并将其组合成一个完整的神经网络模型。 -
管理模型的参数:
nn.Module
可以跟踪并管理模型的所有参数,使得我们可以方便地进行参数更新和保存。 -
提供其他工具函数:
nn.Module
还提供了一些其他的工具函数,如dropout、batch normalization等,以及一些辅助函数,如计算参数数量、移动模型到GPU等。import torch.nn as nn """ 定义了一个Net类,并将nn.Module作为其父类。 Net类包含了两个卷积层、三个全连接层和一个池化层。 在__init__()方法中,我们定义了所有的层,并将它们保存到模型的属性中。 在forward()方法中,我们定义了输入数据向前传播的过程。 当我们需要训练或测试模型时,只需要像调用普通函数一样调用该模型即可。 """ class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(3, 6, 5) self.pool = nn.MaxPool2d(2, 2) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16 * 5 * 5, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = self.pool(F.relu(self.conv2(x))) x = x.view(-1, 16 * 5 * 5) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x
3 llama模型
链接:https://zhuanlan.zhihu.com/p/649756898
1 处理流程
首先在了解Llama 2模型结构细节之前,我们先来看一看大语言模型通常的处理流程:
输入数据:LLM的输入数据是一段文本,可以是一个句子或一段话。文本通常被表示成单词或字符的序列。
[君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。...五花马、千金裘,呼儿将出换美酒,与尔同销万古愁]
Tokenization:之后需要将文本进行Tokenization,将其切分成单词或字符,形成Token序列。之后再将文本映射成模型可理解的输入形式,将文本序列转换为整数索引序列(这个索引就是单词或字符在语料库中的index),这个过程通常由一些开源的文本Tokenzier工具,如sentencepiece等来处理
序列化->
['BOS','君','不','见','黄','河','之','水','天','上','来',',' ,'奔','流','到'...'与','尔','同','销','万','古','愁','EOS']
假设语料库索引化->
['BOS','10','3','67','89','21','45','55','61','4','324','565' ,'789','6567','786'...'7869','9','3452','563','56','66','77','EOS']
Embedding:文本信息经过Tokenization之后变成了token序列,而Embedding则继续将每个Token映射为一个实数向量,为Embeding Vector
'BOS'-> [p_{00},p_{01},p_{02},...,p_{0d-1}]
'10' -> [p_{10},p_{11},p_{12},...,p_{1d-1}]
'3' -> [p_{20},p_{21},p_{22},...,p_{2d-1}]
...
'EOS'-> [p_{n0},p_{n1},p_{n2},...,p_{nd-1}]
位置编码:对于Token序列中的每个位置,添加位置编码(Positional Encoding)向量,以提供关于Token在序列中位置的信息。位置编码是为了区分不同位置的Token,并为模型提供上下文关系的信息。
[p_{00},p_{01},p_{02},...,p_{0d-1}] [pe_{00},pe_{01},pe_{02},...,pe_{0d-1}]
[p_{10},p_{11},p_{12},...,p_{1d-1}] [pe_{10},pe_{11},pe_{12},...,pe_{1d-1}]
[p_{20},p_{21},p_{22},...,p_{2d-1}] + [pe_{20},pe_{21},pe_{22},...,pe_{2d-1}]
... ...
[p_{n0},p_{n1},p_{n2},...,p_{nd-1}] [pe_{n0},pe_{n1},pe_{n2} ,...,pe_{nd-1}]
Transformer :在生成任务中,模型只需要用到Transformer 的decoder阶段,即Decoder-Only,比如GPT、LLaMA 都是。
自回归生成:在生成任务中,使用自回归(Autoregressive)方式,即逐个生成输出序列中的每个Token。在解码过程中,每次生成一个Token时,使用前面已生成的内容作为上下文,来帮助预测下一个Token。
输出处理:生成的Token序列通过一个输出层,通常是线性变换加上Softmax函数,将每个位置的概率分布转换为对应Token的概率。根据概率,选择概率最高的Token或者作为模型的预测结果。或者其他的的方法生成next token
在llama2模型中,当设置output_hidden_states=True
时,模型会返回每一层的隐藏状态。这些隐藏状态被称为"hidden_states"。
hidden_states[1:]
表示从第二层开始的所有隐藏状态。通常,索引0的隐藏状态是输入层的隐藏状态,而后续的隐藏状态对应于模型中的不同层。
每个隐藏状态是一个张量(tensor),可以看作是一个多维数组。其中,第一个维度表示批次大小(batch size),第二个维度表示输入序列长度(token sequence length),而剩余的维度则表示隐藏状态的维度。
以下是一个示例,假设我们使用llama2模型处理一个批次大小为2、输入序列长度为5的文本:
hidden_states = [
# 第一层隐藏状态(输入层)
tensor([[[0.1, 0.2, 0.3, ...], # 第1个token的隐藏状态
[0.4, 0.5, 0.6, ...], # 第2个token的隐藏状态
...
[0.7, 0.8, 0.9, ...]], # 第5个token的隐藏状态
[[0.2, 0.3, 0.4, ...], # 第1个token的隐藏状态
[0.5, 0.6, 0.7, ...], # 第2个token的隐藏状态
...
[0.8, 0.9, 1.0, ...]]]),# 第5个token的隐藏状态
# 第二层隐藏状态
tensor([[[0.2, 0.3, 0.4, ...],
[0.5, 0.6, 0.7, ...],
...
[0.8, 0.9, 1.0, ...]],
[[0.3, 0.4, 0.5, ...],
[0.6, 0.7, 0.8, ...],
...
[0.9, 1.0, 1.1, ...]]]),
# 其他层隐藏状态...
]
hidden_states[0]
表示输入层的隐藏状态,hidden_states[1]
表示第一层隐藏状态,以此类推。每个隐藏状态都是一个形状为(2, 5, d)
的张量,其中d
表示隐藏状态的维度。
4 FeedForward实现训练
import numpy as np
class FeedForwardNetwork:
def __init__(self, input_size, hidden_size, output_size):
self.weights1 = np.random.randn(input_size, hidden_size)
self.bias1 = np.zeros(hidden_size)
self.weights2 = np.random.randn(hidden_size, output_size)
self.bias2 = np.zeros(output_size)
def forward(self, x):
self.hidden_output = np.dot(x, self.weights1) + self.bias1
self.hidden_activation = self._sigmoid(self.hidden_output)
output = np.dot(self.hidden_activation, self.weights2) + self.bias2
return output
def _sigmoid(self, x):
return 1 / (1 + np.exp(-x))
X = np.array([[0, 0,1], [0, 1,1], [1, 0,1], [1, 1,1]])
y = np.array([[0], [1], [1], [0]])
# 创建神经网络
network = FeedForwardNetwork(input_size=3, hidden_size=2, output_size=2)
print(network.weights1)
print(network.weights2)
print(network.bias1)
# 训练参数设置
epochs = 100
learning_rate = 0.1
for epoch in range(epochs):
# 前向传播
output = network.forward(X)
print("hidden_activation:",network.hidden_activation)
# 计算损失
loss = np.mean((output - y) ** 2)
# 打印损失
if epoch % 100 == 0:
print(f"Epoch {epoch}: Loss = {loss}")
# 反向传播更新权重和偏置
d_loss = 2 * (output - y)
d_activation = network._sigmoid(output) * (1 - network._sigmoid(output))
d_output = d_loss * d_activation
d_weights2 = np.dot(network.hidden_activation.T, d_output)
d_bias2 = np.sum(d_output, axis=0)
d_hidden = np.dot(d_output, network.weights2.T)
d_hidden_activation = network._sigmoid(network.hidden_output) * (1 - network._sigmoid(network.hidden_output))
d_hidden_output = d_hidden * d_hidden_activation
d_weights1 = np.dot(X.T, d_hidden_output)
d_bias1 = np.sum(d_hidden_output, axis=0)
network.weights2 -= learning_rate * d_weights2
network.bias2 -= learning_rate * d_bias2
network.weights1 -= learning_rate * d_weights1
network.bias1 -= learning_rate * d_bias1
# 预测
output = network.forward(X)
print("Predictions:", output)