diff --git a/2024/01/23/develop_go_kit/index.html b/2024/01/23/develop_go_kit/index.html index c7bab19..39a0227 100644 --- a/2024/01/23/develop_go_kit/index.html +++ b/2024/01/23/develop_go_kit/index.html @@ -494,9 +494,9 @@

实际运行结果

- + - 【深度学习】DeepL知识汇总 + 【后端开发】Consul服务与配置 Previous diff --git a/2024/01/30/dl_summary/index.html b/2024/01/30/dl_summary/index.html deleted file mode 100644 index 62aff66..0000000 --- a/2024/01/30/dl_summary/index.html +++ /dev/null @@ -1,909 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 【深度学习】DeepL知识汇总 - LIHAIBIN'S BLOG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- - - - - - -
- -
- -
- - - -
-
-
- - -
- -
-
-
-
-

【深度学习】DeepL知识汇总

- -

- - - Last updated on March 14, 2024 am - - -

- - -
- -

深度学习知识汇总

-

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

- -

训练与模型

- -

逻辑回归和线性回归

-

线性回归解决的是回归问题,逻辑回归相当于是线性回归的基础上,来解决分类问题

-线性回归(Linear Regression) \[ -\begin{aligned} -&f_{w, b}(x)=\sum_i w_i x_i+b\\ -\end{aligned} -\] 逻辑回归(Logistic Regression) $$ -\[\begin{aligned} - -&f_{w, b}(x)=\sigma\left(\sum_i w_i x_i+b\right) -\end{aligned}\] -

$$ 逻辑回归可以理解为在线性回归后加了一个 sigmoid -函数。将线性回归变成一个0~1输出的分类问题。逻辑回归本质上是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归的。可以说,逻辑回归都是以线性回归为理论支持的,只不过逻辑回归可以轻松解决 -0/1 分类问题。

-

深度学习模型的参数范围

-

因为参数越小代表模型越简单,越是复杂的模型,越是尝试对所有样本进行拟合,包括异常点。这就会造成在较小的区间中产生较大的波动,这个较大的波动也会反映在这个区间的导数比较大。只有越大的参数才可能产生较大的导数。因此参数越小,模型就越简单。

-

实现参数稀疏

-

参数的稀疏,在一定程度上实现了特征的选择。一般而言,大部分特征对模型是没有贡献的。这些没有用的特征虽然可以减少训练集上的误差,但是对测试集的样本,反而会产生干扰。稀疏参数的引入,可以将那些无用的特征的权重置为0

-

Batch -size的大小对学习率的影响

-
    -
  • batch-size大,学习率也可以取得大一点,而且,batch-size大通常更新次数少,所以需要更多的epoch才能让loss收敛。
  • -
  • batch-size小,学习率应该取得小一点,取的大会发生nan(梯度爆炸了),batch-size小通常更新次数多,较少的epoch就课可以让loss收敛,但是缺点是训练过程慢。
  • -
-

为什么batch-size小,学习率取的大会发生nan? -学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)

-

优化器于损失函数

-

优化器optimizer和损失函数loss function的区别:

-
    -
  1. 优化器定义了哪些参数是要用来更新的,并且设置了更新的方式(学习率、动量、SGD等),还有一些权重衰减的设置。
  2. -
  3. 损失函数是用来计算损失的,也可以说损失函数是负责反向传播求导用的
  4. -
-

训练中loss快速增大

-

训练过程中发现loss快速增大应该从哪些方面考虑?

-
    -
  1. 学习率过大,导致学习的过程非常不平稳导致的损失值快速增大
  2. -
  3. 训练的样本中存在坏数据,造成了数据的污染
  4. -
-

梯度消失和梯度爆炸

-

梯度消失的原因和解决

-
    -
  1. 隐藏层的层数过多

    -

    反向传播求梯度时的链式求导法则,某部分梯度小于1,则多层连乘后出现梯度消失

  2. -
  3. 采用了不合适的激活函数

    -

    如sigmoid函数的最大梯度为1/4,这意味着隐藏层每一层的梯度均小于1(权值小于1时),出现梯度消失

  4. -
  5. 解决方法:1、relu激活函数,使导数衡为1 2、batch norm -3、残差结构

  6. -
-

梯度爆炸的原因和解决办法

-
    -
  1. 隐藏层的层数过多,某部分梯度大于1,则多层连乘后,梯度呈指数增长,产生梯度爆炸。
  2. -
  3. 权重初始值太大,求导时会乘上权重
  4. -
  5. 解决方法:1、梯度裁剪 2、权重L1/L2正则化 3、残差结构 4、batch -norm
  6. -
-

如何取消张量梯度

-

可以选择使用torch.no_grad来将某个张量的值取消计算梯度,model.eval -vs和torch.no_grad区别

-
    -
  • model.eval(): -依然计算梯度,但是不反传;dropout层保留概率为1;batchnorm层使用全局的mean和var
  • -
  • with torch.no_grad: 不计算梯度
  • -
-

Dropout和BatchNorm

-

Dropout和Batch norm能否一起使用?

-

可以,但是只能将Dropout放在Batch -norm之后使用。因为Dropout训练时会改变输入X的方差,从而影响Batch -norm训练过程中统计的滑动方差值;而测试时没有Dropout,输入X的方差和训练时不一致,这就导致Batch -norm测试时期望的方差和训练时统计的有偏差。

-

PyTorch实现基础网络

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

-

-

PyTorch几类优化器

- -

Adam与SGD的区别

-

SGD缺点是其更新方向完全依赖于当前batch计算出的梯度,因而十分不稳定。

-

Adam的优点主要在于:

-
    -
  • 考虑历史步中的梯度更新信息,能够降低梯度更新噪声。
  • -
  • 此外经过偏差校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
  • -
-

但是Adam也有其自身问题:可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。二者似乎都没法很好避免局部最优问题。

-

Norm归一化

- -

Batch Norm代码实现

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta
-

Transformer部分

- -

残差结构的设计

-

残差结构设计思想:残差网络的本质也是解决梯度消失/爆炸的问题,只不过是在网络结构层面的改变 -残差网络的出现解决了构建深层神经网络时网络退化即梯度消失/爆炸的问题。残差结构主要设计有两个,快捷连接(shortcut -connection)和恒等映射(identity -mapping),快捷连接使得残差变得可能,而恒等映射使得网络变深,恒等映射主要有两个:跳跃连接和激活函数

-

PyTorch实现Attention

-

自注意力机制的实现

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

-

多头注意力机制的实现

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)
-

PyTorch实现Transformer

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import torch
import torch.nn as nn
import torch.nn.functional as F

class MultiHeadAttention(nn.Module):
def __init__(self, embed_dim, num_heads):
super(MultiHeadAttention, self).__init__()
self.num_heads = num_heads
self.head_dim = embed_dim // num_heads

self.query_fc = nn.Linear(embed_dim, embed_dim)
self.key_fc = nn.Linear(embed_dim, embed_dim)
self.value_fc = nn.Linear(embed_dim, embed_dim)
self.fc_out = nn.Linear(embed_dim, embed_dim)

def forward(self, query, key, value, mask=None):
batch_size = query.shape[0]

# Linearly project queries, keys, and values
Q = self.query_fc(query)
K = self.key_fc(key)
V = self.value_fc(value)

# Split the embedding into num_heads
Q = Q.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)

# Calculate the attention scores
scores = torch.matmul(Q, K.permute(0, 1, 3, 2)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))

if mask is not None:
scores = scores.masked_fill(mask == 0, float("-1e20"))

# Apply softmax to get attention probabilities
attention_weights = F.softmax(scores, dim=-1)

# Apply dropout
attention_weights = F.dropout(attention_weights, p=0.1, training=self.training)

# Multiply the attention weights with the values
output = torch.matmul(attention_weights, V)

# Concatenate multi-heads and project
output = output.permute(0, 2, 1, 3).contiguous().view(batch_size, -1, embed_dim)
output = self.fc_out(output)

return output, attention_weights

class PositionwiseFeedforward(nn.Module):
def __init__(self, embed_dim, hidden_dim):
super(PositionwiseFeedforward, self).__init__()
self.fc1 = nn.Linear(embed_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, embed_dim)

def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x

class EncoderLayer(nn.Module):
def __init__(self, embed_dim, num_heads, hidden_dim):
super(EncoderLayer, self).__init__()
self.multihead_attention = MultiHeadAttention(embed_dim, num_heads)
self.feed_forward = PositionwiseFeedforward(embed_dim, hidden_dim)
self.layer_norm1 = nn.LayerNorm(embed_dim)
self.layer_norm2 = nn.LayerNorm(embed_dim)

def forward(self, x, mask=None):
# Multi-Head Attention
residual = x
x, _ = self.multihead_attention(x, x, x, mask)
x = self.layer_norm1(x + residual)

# Feed Forward
residual = x
x = self.feed_forward(x)
x = self.layer_norm2(x + residual)

return x

class TransformerEncoder(nn.Module):
def __init__(self, vocab_size, embed_dim, num_layers, num_heads, hidden_dim):
super(TransformerEncoder, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.layers = nn.ModuleList([EncoderLayer(embed_dim, num_heads, hidden_dim) for _ in range(num_layers)])

def forward(self, x, mask=None):
x = self.embedding(x)
for layer in self.layers:
x = layer(x, mask)
return x

-

强化学习部分

-

PyTorch实现DQN

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

-

PyTorch实现PPO

-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

-

自动驾驶和RL联系与设计

-

设计和实现一个基于强化学习的自动驾驶决策规划系统是一个复杂而且需要深入思考的任务。下面是一个基本的设计和实现方案:

-
设计方案
-
    -
  1. 环境建模: -首先,需要对自动驾驶的环境进行建模,包括车辆、道路、交通规则、障碍物、目标等。环境可以使用基于物理的仿真环境,例如CARLA,或者基于虚拟仿真的环境。
  2. -
  3. 状态空间定义: -定义自动驾驶车辆的状态空间,这可能包括车辆的位置、速度、方向、周围车辆的位置和速度等信息。
  4. -
  5. 动作空间定义: -定义自动驾驶车辆可以执行的动作空间,如加速、减速、转向等。
  6. -
  7. 奖励函数设计: -设计奖励函数来评估每个状态下执行的动作,以指导智能体的学习。奖励函数应该考虑安全性、效率、舒适性等因素。
  8. -
  9. 智能体模型选择: -选择合适的强化学习算法和模型架构来训练智能体,例如深度Q网络(DQN)、深度确定性策略梯度(DDPG)、双重深度确定性策略梯度(TD3)等。
  10. -
  11. 训练策略: -定义训练策略,包括学习率、优化器、探索策略等。
  12. -
  13. 评估与测试: -在仿真环境中对训练好的智能体进行评估和测试,检查其性能和鲁棒性。
  14. -
-
实现方案
-
    -
  1. 环境建模: 使用CARLA等仿真平台进行环境建模。
  2. -
  3. 状态空间和动作空间定义: -编写代码从环境中获取车辆状态信息,并定义可以执行的动作。
  4. -
  5. 奖励函数设计: -根据项目需求和目标设计奖励函数。
  6. -
  7. 智能体模型选择: -根据任务选择合适的强化学习算法和模型架构,例如使用深度神经网络作为Q值函数的估计器。
  8. -
  9. 训练智能体: -使用收集的数据对智能体进行训练,调整网络参数,以最大化奖励函数。
  10. -
  11. 评估与测试: -在仿真环境中评估训练后的智能体的性能,分析其行为是否符合预期,并且能否有效应对各种情况。
  12. -
  13. 迭代优化: -根据评估结果,对模型和训练策略进行调整和优化,不断改进智能体的性能和稳定性。
  14. -
-

以上是一个基本的设计和实现方案,实际项目中可能还涉及到更多的细节和挑战,如安全性保障、仿真环境与真实环境的一致性等问题。因此,在实施过程中需要综合考虑各种因素,以确保系统的稳定性和安全性。

-

LLAMA2部分

- -

bert模型细节 https://www.zhihu.com/question/534763354

-

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

-

Qlora https://zhuanlan.zhihu.com/p/618894919

-

RLHF https://zhuanlan.zhihu.com/p/631238431

-

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

-

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

-

fine-tuning -llama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

-

fine-tuning Llama 2 with PEFT's QLoRahttps://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

-

常见考察题

-

神经网络权重初始化 https://blog.csdn.net/kebu12345678/article/details/103084851

-

https://zhuanlan.zhihu.com/p/667048896

-

https://zhuanlan.zhihu.com/p/643560888

-

https://zhuanlan.zhihu.com/p/599016986)

-

softmax如何防止指数上溢

-

在计算softmax函数时,指数上溢是一个常见的问题,特别是当输入的数值非常大时,指数函数的计算结果可能会溢出。为了解决这个问题,可以采取以下几种方法:

-
    -
  1. 数值稳定性技巧:为了避免指数函数的溢出,可以将输入的数值减去一个常数,使得输入相对较小,从而减少指数函数的值。通常,可以通过找到输入向量中的最大值,并将所有元素减去这个最大值来实现数值稳定性。

    -

    image-20240222173542613

    -

    这样做可以保持相对稳定,防止指数函数的溢出。

  2. -
  3. 利用性质:softmax函数的分子和分母同时除以一个相同的常数并不会改变函数的值。因此,我们可以在计算softmax时,将所有输入向量的值都减去向量中的最大值,然后进行softmax计算。

  4. -
-

以上两种方法都可以有效地避免指数上溢的问题,并保持softmax函数的数值稳定性。在实际应用中,通常会使用这些技巧来计算softmax函数,以确保模型的稳定性和数值精度。

- - -
- -
-
- - - - - - -
-
-
【深度学习】DeepL知识汇总
-
https://lihaibineric.github.io/2024/01/30/dl_summary/
-
-
- -
-
Author
-
Haibin Li
-
- - -
-
Posted on
-
January 30, 2024
-
- - -
-
Updated on
-
March 14, 2024
-
- - -
-
Licensed under
-
- - - - - - - - - - -
-
- -
-
-
- - - - -
- - -
- -
- - -
-
-
-
- -
- - - - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/2024/02/02/develop_consul/index.html b/2024/02/02/develop_consul/index.html index bb02723..cd1755f 100644 --- a/2024/02/02/develop_consul/index.html +++ b/2024/02/02/develop_consul/index.html @@ -597,8 +597,8 @@

Consul持久性

- - 【深度学习】DeepL知识汇总 + + 【后端开发】Go-kit与Gin框架 Next diff --git a/2024/03/05/dl_llm_basic/index.html b/2024/03/05/dl_llm_basic/index.html index 22d5e11..3d56f0a 100644 --- a/2024/03/05/dl_llm_basic/index.html +++ b/2024/03/05/dl_llm_basic/index.html @@ -16,12 +16,12 @@ - + - + @@ -29,7 +29,7 @@ - + @@ -214,7 +214,7 @@ @@ -225,7 +225,7 @@ - 290 mins + 408 mins @@ -276,7 +276,93 @@

【深度学习】DeepL|LLM基础知识

-

深度学习&LLM基础

+

这里将分享深度学习和大语言模型的基础知识和内容

+

深度学习基础

+

训练细节

+

1.逻辑回归和线性回归

+

线性回归解决的是回归问题,逻辑回归相当于是线性回归的基础上,来解决分类问题

+线性回归(Linear Regression) \[ +\begin{aligned} +&f_{w, b}(x)=\sum_i w_i x_i+b\\ +\end{aligned} +\] 逻辑回归(Logistic Regression) $$ +\[\begin{aligned} + +&f_{w, b}(x)=\sigma\left(\sum_i w_i x_i+b\right) +\end{aligned}\] +

$$ 逻辑回归可以理解为在线性回归后加了一个 sigmoid +函数。将线性回归变成一个0~1输出的分类问题。逻辑回归本质上是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归的。可以说,逻辑回归都是以线性回归为理论支持的,只不过逻辑回归可以轻松解决 +0/1 分类问题。

+

2. 深度学习模型的参数范围

+

因为参数越小代表模型越简单,越是复杂的模型,越是尝试对所有样本进行拟合,包括异常点。这就会造成在较小的区间中产生较大的波动,这个较大的波动也会反映在这个区间的导数比较大。只有越大的参数才可能产生较大的导数。因此参数越小,模型就越简单。

+

3. 实现参数稀疏

+

参数的稀疏,在一定程度上实现了特征的选择。一般而言,大部分特征对模型是没有贡献的。这些没有用的特征虽然可以减少训练集上的误差,但是对测试集的样本,反而会产生干扰。稀疏参数的引入,可以将那些无用的特征的权重置为0

+

4. Batch +size的大小对学习率的影响

+
    +
  • batch-size大,学习率也可以取得大一点,而且,batch-size大通常更新次数少,所以需要更多的epoch才能让loss收敛。
  • +
  • batch-size小,学习率应该取得小一点,取的大会发生nan(梯度爆炸了),batch-size小通常更新次数多,较少的epoch就课可以让loss收敛,但是缺点是训练过程慢。
  • +
+

为什么batch-size小,学习率取的大会发生nan? +学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)

+

5. 优化器于损失函数

+

优化器optimizer和损失函数loss function的区别:

+
    +
  1. 优化器定义了哪些参数是要用来更新的,并且设置了更新的方式(学习率、动量、SGD等),还有一些权重衰减的设置。
  2. +
  3. 损失函数是用来计算损失的,也可以说损失函数是负责反向传播求导用的
  4. +
+

6. 训练中loss快速增大

+

训练过程中发现loss快速增大应该从哪些方面考虑?

+
    +
  1. 学习率过大,导致学习的过程非常不平稳导致的损失值快速增大
  2. +
  3. 训练的样本中存在坏数据,造成了数据的污染
  4. +
+

7. 梯度消失和梯度爆炸

+

梯度消失的原因和解决

+
    +
  1. 隐藏层的层数过多

    +

    反向传播求梯度时的链式求导法则,某部分梯度小于1,则多层连乘后出现梯度消失

  2. +
  3. 采用了不合适的激活函数

    +

    如sigmoid函数的最大梯度为1/4,这意味着隐藏层每一层的梯度均小于1(权值小于1时),出现梯度消失

  4. +
  5. 解决方法:1、relu激活函数,使导数衡为1 2、batch norm +3、残差结构

  6. +
+

梯度爆炸的原因和解决办法

+
    +
  1. 隐藏层的层数过多,某部分梯度大于1,则多层连乘后,梯度呈指数增长,产生梯度爆炸。
  2. +
  3. 权重初始值太大,求导时会乘上权重
  4. +
  5. 解决方法:1、梯度裁剪 2、权重L1/L2正则化 3、残差结构 4、batch +norm
  6. +
+

8. 如何取消张量梯度

+

可以选择使用torch.no_grad来将某个张量的值取消计算梯度,model.eval +vs和torch.no_grad区别

+
    +
  • model.eval(): +依然计算梯度,但是不反传;dropout层保留概率为1;batchnorm层使用全局的mean和var
  • +
  • with torch.no_grad: 不计算梯度
  • +
+

9.Dropout和BatchNorm

+

Dropout和Batch norm能否一起使用?

+

可以,但是只能将Dropout放在Batch +norm之后使用。因为Dropout训练时会改变输入X的方差,从而影响Batch +norm训练过程中统计的滑动方差值;而测试时没有Dropout,输入X的方差和训练时不一致,这就导致Batch +norm测试时期望的方差和训练时统计的有偏差。

+

其余参考

+

神经网络权重初始化 https://blog.csdn.net/kebu12345678/article/details/103084851

+

https://zhuanlan.zhihu.com/p/667048896

+

https://zhuanlan.zhihu.com/p/643560888

+

https://zhuanlan.zhihu.com/p/599016986

+ +

LLM基础

1.Attention

1.1 讲讲对Attention的理解

@@ -785,7 +871,74 @@

3.8 Post-LN 和 Pre-LN

Norm更容易训练好理解,因为它的恒等路径更突出,但为什么它效果反而没那么好呢?这个是解释的链接:https://kexue.fm/archives/9009

4 PyTorch几类优化器

-

4.1 Batch Norm

+

4.1 梯度下降 (Gradient Descent, +GD):

+
    +
  • 公式表达: \(\theta_{t+1}=\theta_t-\alpha \nabla +J\left(\theta_t\right)\)

  • +
  • 其中, \(\theta_t\) 是第 \(t\) 步的模型参数, \(\alpha\) 是学习率, \(J\left(\theta_t\right)\) 是损失函数, \(\nabla J\left(\theta_t\right)\) +是损失函数关于参数的梯度。 ## 4.2 随机梯度下降 (SGD):

  • +
  • 公式表达: \(\theta_{t+1}=\theta_t-\alpha \nabla +J\left(\theta_t ; x^{(i)}, y^{(i)}\right)\)

  • +
  • 与梯度下降不同的是, 每次更新时只随机选取一个样本 \(\left(x^{(i)}, y^{(i)}\right)\) +来计算梯度。

  • +
  • 优点:

    +

        虽然SGD需要走很多步的样子,但是对梯度的要求很低(计算梯度快)。而对于引入噪声,大量的理论和实践工作证明,只要噪声不是特别大,SGD都能很好地收敛。应用大型数据集时,训练速度很快。比如每次从百万数据样本中,取几百个数据点,算一个SGD梯度,更新一下模型参数。相比于标准梯度下降法的遍历全部样本,每输入一个样本更新一次参数,要快得多。

    +

    缺点:

    +

        SGD在随机选择梯度的同时会引入噪声,使得权值更新的方向不一定正确。此外,SGD也没能单独克服局部最优解的问题。 +## 4.3 带动量的梯度下降 (Momentum):

  • +
  • 公式表达: \(v_{t+1}=\beta v_t+(1-\beta) +\nabla J\left(\theta_t\right), \theta_{t+1}=\theta_t-\alpha +v_{t+1}\)

  • +
  • \(v_t\) 是动量, \(\beta\) 是动量系数。

  • +
  • 动量主要解决SGD的两个问题:一是随机梯度的方法(引入的噪声);二是Hessian矩阵病态问题(可以理解为SGD在收敛过程中和正确梯度相比来回摆动比较大的问题)。

    +

        简单理解:由于当前权值的改变会受到上一次权值改变的影响,类似于小球向下滚动的时候带上了惯性。这样可以加快小球向下滚动的速度。 +## 4.4 自适应学习率优化器 :

  • +
+

RMSprop (Root Mean Square +Propagation):

+
    +
  • 公式表达: \(v_{t+1}=\beta +v_t+(1-\beta)\left(\nabla J\left(\theta_t\right)\right)^2, +\theta_{t+1}=\theta_t-\frac{\alpha}{\sqrt{v_{t+1}}+\epsilon} \nabla +J\left(\theta_t\right)\)

  • +
  • 更新权重的时候,使用除根号的方法,可以使较大的梯度大幅度变小,而较小的梯度小幅度变小,这样就可以使较大梯度方向上的波动小下来,那么整个梯度下降的过程中摆动就会比较小,就能设置较大的learning-rate,使得学习步子变大,达到加快学习的目的。

    +

        在实际的应用中,权重W或者b往往是很多维度权重集合,就是多维的,在进行除根号操作中,会将其中大的维度的梯度大幅降低,不是说权重W变化趋势一样。

  • +
+

Adam (Adaptive Moment +Estimation):

+
    +
  • 公式表达: \(m_{t+1}=\beta_1 +m_t+\left(1-\beta_1\right) \nabla J\left(\theta_t\right), +v_{t+1}=\beta_2 v_t+\left(1-\beta_2\right)\left(\nabla +J\left(\theta_t\right)\right)^2\), \(\theta_{t+1}=\theta_t-\frac{\alpha}{\sqrt{v_{t+1}}+\epsilon} +\frac{m_{t+1}}{1-\beta_1^{t+1}}\)

  • +
  • 在这两种算法中, \(v_t\)\(m_t\) +分别是平方梯度的指数移动平均和梯度的指数移动平均。 \(\beta_1\)\(\beta_2\)是动量和梯度平方的衰减率。

  • +
  • Adam的优点主要在于:

    +
      +
    • 考虑历史步中的梯度更新信息,能够降低梯度更新噪声。
    • +
    • 此外经过偏差校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
    • +
    +

    但是Adam也有其自身问题:可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。二者似乎都没法很好避免局部最优问题。

  • +
+

这些优化器之间的区别主要体现在对梯度的处理方式上, +以及对学习率的自适应调整。例如, +动量优化器可以加速收玫并帮助逃离局部最优点, +而自适应学习率优化器则可以根据梯度情况动态调整学习率, +适应不同参数的更新速度。

5.位置编码

5.1 绝对位置编码

不同于RNN、CNN等模型,对于Transformer模型来说,位置编码的加入是必不可少的,因为纯粹的Attention模块是无法捕捉输入顺序的,即无法区分不同位置的Token。为此我们大体有两个选择:

@@ -1526,6 +1679,47 @@

(4)进制转换

alt="进制转换的示意图" /> +

其他文章参考

+

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

+ +

LLAMA2结构https://blog.csdn.net/sikh_0529/article/details/134375318

+

旋转位置嵌入https://www.zhihu.com/tardis/zm/art/647109286?source_id=1005

+

bert模型细节 https://www.zhihu.com/question/534763354

+

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

+

Qlora https://zhuanlan.zhihu.com/p/618894919

+

RLHF https://zhuanlan.zhihu.com/p/631238431

+

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

+

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

+

fine-tuning +llama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

+

fine-tuning Llama 2 with PEFT's +QLoRa[https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/](

+

基础代码

+

PyTorch实现基础网络

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

+

Batch Norm代码实现

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta
+

PyTorch实现Attention

+

自注意力机制的实现

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

+

多头注意力机制的实现

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)
+

PyTorch实现DQN

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

+

PyTorch实现PPO

+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

diff --git a/2024/03/08/dl_llm_model/index.html b/2024/03/08/dl_llm_model/index.html index b9faa82..ead9791 100644 --- a/2024/03/08/dl_llm_model/index.html +++ b/2024/03/08/dl_llm_model/index.html @@ -901,9 +901,9 @@

3.8
- + - 【大语言模型】有监督微调 + 【大语言模型】大模型监督微调 Previous diff --git a/2024/03/24/dl-llm-ft/index.html b/2024/03/24/dl-llm-ft/index.html index 2d81ca1..0109601 100644 --- a/2024/03/24/dl-llm-ft/index.html +++ b/2024/03/24/dl-llm-ft/index.html @@ -18,7 +18,7 @@ - + @@ -39,7 +39,7 @@ - + @@ -48,7 +48,7 @@ - 【大语言模型】有监督微调 - LIHAIBIN'S BLOG + 【大语言模型】大模型监督微调 - LIHAIBIN'S BLOG @@ -202,7 +202,7 @@

在Transformer的Multi-HeadAttention中,对每个head进行降维是为了增加模型的表达能力和效率。

每个head是独立的注意力机制,它们可以学习不同类型的特征和关系。通过使用多个注意力头,Transformer可以并行地学习多种不同的特征表示,从而增强了模型的表示能力。

然而,在使用多个注意力头的同时,注意力机制的计算复杂度也会增加。原始的ScaledDot-Product Attention的计算复杂度为\(O(d^2)\),其中d是输入向量的维度。如果使用h个注意力头,计算复杂度将增加到\(O(hd^2)\)。这可能会导致Transformer在处理大规模输入时变得非常耗时。

为了缓解计算复杂度的问题,Transformer中在每个head上进行降维。在每个注意力头中,输入向量通过线性变换被映射到一个较低维度的空间。这个降维过程使用两个矩阵:一个是查询(Q)和键(K)的降维矩阵\(W_q\)和\(W_k\),另一个是值(V)的降维矩阵\(W_v\)。

通过降低每个head的维度,Transformer可以在保持较高的表达能力的同时,大大减少计算复杂度。降维后的计算复杂度为\((h\hat d ^ 2)\),其中\(\hat d\)是降维后的维度。通常情况下,\(\hatd\)会远小于原始维度d,这样就可以显著提高模型的计算效率。

2.2transformer在哪里做了权重共享,为什么可以做权重共享?

Transformer在Encoder和Decoder中都进行了权重共享。

在Transformer中,Encoder和Decoder是由多层的Self-AttentionLayer和前馈神经网络层交叉堆叠而成。权重共享是指在这些堆叠的层中,相同位置的层共用相同的参数

在Encoder中,所有的自注意力层和前馈神经网络层都共享相同的参数。这意味着每一层的自注意力机制和前馈神经网络都使用相同的权重矩阵来进行计算。这种共享保证了每一层都执行相同的计算过程,使得模型能够更好地捕捉输入序列的不同位置之间的关联性。

在Decoder中,除了和Encoder相同的权重共享方式外,还存在另一种特殊的权重共享:Decoder的自注意力层和Encoder的自注意力层之间也进行了共享。这种共享方式被称为"maskedself-attention",因为在解码过程中,当前位置的注意力不能关注到未来的位置(后续位置),以避免信息泄漏。通过这种共享方式,Decoder可以利用Encoder的表示来理解输入序列并生成输出序列。权重共享的好处是大大减少了模型的参数数量,使得Transformer可以更有效地训练,并且更容易进行推理。此外,共享参数还有助于加快训练速度和提高模型的泛化能力,因为模型可以在不同位置共享并学习通用的特征表示。

2.3Transformer的点积模型做缩放的原因是什么?

使用缩放的原因是为了控制注意力权重的尺度,以避免在计算过程中出现梯度爆炸的问题。

Attention的计算是在内积之后进行softmax,主要涉及的运算是\(e^{q \cdotk}\),可以大致认为内积之后、softmax之前的数值在\(-3\sqrt{d}\)到\(3\sqrt{d}\)这个范围内,由于d通常都至少是64,所以\(e^{3\sqrt{d}}\)比较大而 \(e^{-3\sqrt{d}}\)比较小,因此经过softmax之后,Attention的分布非常接近一个onehot分布了,这带来严重的梯度消失问题,导致训练效果差。(例如y=softmax(x)在|x|较大时进入了饱和区,x继续变化y值也几乎不变,即饱和区梯度消失)

相应地,解决方法就有两个:

  1. 像NTK参数化那样,在内积之后除以 \(\sqrt{d}\),使q⋅k的方差变为1,对应\(e^3,e^{−3}\)都不至于过大过小,这样softmax之后也不至于变成onehot而梯度消失了,这也是常规的Transformer如BERT里边的SelfAttention的做法
  2. 另外就是不除以 \(\sqrt{d}\),但是初始化q,k的全连接层的时候,其初始化方差要多除以一个d,这同样能使得使q⋅k的初始方差变为1,T5采用了这样的做法。

2.4 Transformer和RNN对比

最简单情况:没有残差连接、没有 layernorm、 attention单头、没有投影。看和 RNN 区别

RNN 跟 transformer 异:如何传递序列的信

RNN 是把上一个时刻的信息输出传入下一个时候做输入。Transformer通过一个 attention 层,去全局的拿到整个序列里面信息,再用 MLP做语义的转换。

RNN 跟 transformer 同:语义空间的转换 + 关注点

用一个线性层 or 一个 MLP 来做语义空间的转换。

关注点:怎么有效的去使用序列的信息。

2.5 Transformer结构细节

Transformer为何使用多头注意力机制?(为什么不使用一个头)

Transformer为什么Q和K使用不同的权重矩阵生成,为何不能使用同一个值进行自身的点乘?(注意和第一个问题的区别)

Transformer计算attention的时候为何选择点乘而不是加法?两者计算复杂度和效果上有什么区别?

为什么在进行softmax之前需要对attention进行scaled(为什么除以dk的平方根),并使用公式推导进行讲解

在计算attentionscore的时候如何对padding做mask操作?

为何在获取输入词向量之后需要对矩阵乘以embeddingsize的开方?意义是什么?

简单介绍一下Transformer的位置编码?有什么意义和优缺点?

你还了解哪些关于位置编码的技术,各自的优缺点是什么?(参考上一题)

为什么transformer块使用LayerNorm而不是BatchNorm?LayerNorm在Transformer的位置是哪里?

简答讲一下BatchNorm技术,以及它的优缺点。

Encoder端和Decoder端是如何进行交互的?(在这里可以问一下关于seq2seq的attention知识)

Decoder阶段的多头自注意力和encoder的多头自注意力有什么区别?(为什么需要decoder自注意力需要进行sequence mask)

引申一个关于bert问题,bert的mask为何不学习transformer在attention处进行屏蔽score的技巧?

3 Normalization

3.1 Batch Norm

为什么要进行BN呢?

  1. 在深度神经网络训练的过程中,通常以输入网络的每一个mini-batch进行训练,这样每个batch具有不同的分布,使模型训练起来特别困难。
  2. Internal Covariate Shift (ICS)问题:在训练的过程中,激活函数会改变各层数据的分布,随着网络的加深,这种改变(差异)会越来越大,使模型训练起来特别困难,收敛速度很慢,会出现梯度消失的问题。

BN的主要思想:针对每个神经元,使数据在进入激活函数之前,沿着通道计算每个batch的均值、方差,‘强迫’数据保持均值为0,方差为1的正态分布,避免发生梯度消失。具体来说,就是把第1个样本的第1个通道,加上第2个样本第1个通道...... 加上第 N 个样本第1个通道,求平均,得到通道 1 的均值(注意是除以N×H×W 而不是单纯除以 N,最后得到的是一个代表这个 batch第1个通道平均值的数字,而不是一个 H×W 的矩阵)。求通道 1的方差也是同理。对所有通道都施加一遍这个操作,就得到了所有通道的均值和方差。

BN的使用位置:全连接层或卷积操作之后,激活函数之前。

BN算法过程:

加入缩放和平移变量的原因是:保证每一次数据经过归一化后还保留原有学习来的特征,同时又能完成归一化操作,加速训练。这两个参数是用来学习的参数。

encoder-decoder分类https://zhuanlan.zhihu.com/p/642923989

BN的作用:

  1. 允许较大的学习率;
  2. 减弱对初始化的强依赖性
  3. 保持隐藏层中数值的均值、方差不变,让数值更稳定,为后面网络提供坚实的基础;
  4. 有轻微的正则化作用(相当于给隐藏层加入噪声,类似Dropout)

BN存在的问题:

  1. 每次是在一个batch上计算均值、方差,如果batchsize太小,则计算的均值、方差不足以代表整个数据分布。
  2. batch size太大:会超过内存容量;需要跑更多的epoch,导致总训练时间变长;会直接固定梯度下降的方向,导致很难更新。

3.2 Layer Norm

LayerNorm是大模型也是transformer结构中最常用的归一化操作,简而言之,它的作用是对特征张量按照某一维度或某几个维度进行0均值,1方差的归一化操作,计算公式为:

\[\mathrm{y}=\frac{\mathrm{x}-\mathrm{E}(\mathrm{x})}{\sqrt{\mathrm{V}\operatorname{ar}(\mathrm{x})+\epsilon}} * \gamma+\beta\]

这里的 \(x\) 可以理解为**张量中具体某一维度的所有元素**,比如对于 shape 为 (2,2,4) 的张量input,若指定归一化的操作为第三个维度,则会对第三个维度中的四个张量(2,2,1),各进行上述的一次计算.

详细形式:

\[a_{i}=\sum_{j=1}^{m} w_{i j} x_{j}, \quady_{i}=f\left(a_{i}+b_{i}\right)\]

\[\bar{a}_{i}=\frac{a_{i}-\mu}{\sigma} g_{i}, \quady_{i}=f\left(\bar{a}_{i}+b_{i}\right),\]

\[\mu=\frac{1}{n} \sum_{i=1}^{n} a_{i}, \quad \sigma=\sqrt{\frac{1}{n}\sum_{i=1}^{n}\left(a_{i}-\mu\right)^{2}}.\]

这里结合PyTorch的nn.LayerNorm算子来看比较明白

1
2
nn.LayerNorm(normalized_shape, eps=1e-05, elementwise_affine=True, device=None, dtype=None)

Layer Normalization (LN)的一个优势是不需要批训练,在单条数据内部就能归一化。LN不依赖于batchsize和输入sequence的长度,因此可以用于batchsize为1和RNN中。LN用于RNN效果比较明显,但是在CNN上,效果不如BN

3.3 Instance Norm

IN针对图像像素做normalization,最初用于图像的风格化迁移。在图像风格化中,生成结果主要依赖于某个图像实例,featuremap 的各个 channel的均值和方差会影响到最终生成图像的风格。所以对整个batch归一化不适合图像风格化中,因而对H、W做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立。

对于,IN 对每个样本的 H、W 维度的数据求均值和标准差,保留 N 、C维度,也就是说,它只在 channel 内部求均值和标准差,其公式如下:

\[y_{t i j k}=\frac{x_{t i j k}-\mu_{t i}}{\sqrt{\sigma_{ti}^{2}+\epsilon}} \quad \mu_{t i}=\frac{1}{H W} \sum_{l=1}^{W}\sum_{m=1}^{H} x_{t i l m} \quad \sigma_{t i}^{2}=\frac{1}{H W}\sum_{l=1}^{W} \sum_{m=1}^{H}\left(x_{t i l m}-m u_{t i}\right)^{2}\]

3.4 Group Norm

GN是为了解决BN对较小的mini-batch size效果差的问题。

GN适用于占用显存比较大的任务,例如图像分割。对这类任务,可能 batchsize 只能是个位数,再大显存就不够用了。而当 batch size 是个位数时,BN的表现很差,因为没办法通过几个样本的数据量,来近似总体的均值和标准差。GN也是独立于 batch 的,它是 LN 和 IN 的折中。

具体方法: GN 计算均值和标准差时,把每一个样本feature map 的 channel 分成 G 组,每组将有 C/G 个 channel,然后将这些channel 中的元素求均值和标准差。各组 channel用其对应的归一化参数独立地归一化。 \[\mu_{n g}(x)=\frac{1}{(C / G) H W} \sum_{c=g C / G}^{(g+1) C / G}\sum_{h=1}^{H} \sum_{w=1}^{W} x_{n c h w}\]

\[\sigma_{n g}(x)=\sqrt{\frac{1}{(C / G) H W} \sum_{c=g C / G}^{(g+1) C /G} \sum_{h=1}^{H} \sum_{w=1}^{W}\left(x_{n c h w}-\mu_{ng}(x)\right)^{2}+\epsilon}\]

3.5 几类归一化区别与联系

Batch Normalization(Batch Norm)缺点:在处理序列数据(如文本)时,BatchNorm可能不会表现得很好,因为序列数据通常长度不一,并且一次训练的Batch中的句子的长度可能会有很大的差异;此外,BatchNorm对于Batch大小也非常敏感。对于较小的Batch大小,BatchNorm可能会表现得不好,因为每个Batch的统计特性可能会有较大的波动。

Layer Normalization(Layer Norm)优点:LayerNorm是对每个样本进行归一化,因此它对Batch大小不敏感,这使得它在处理序列数据时表现得更好;另外,LayerNorm在处理不同长度的序列时也更为灵活。

Instance Normalization(Instance Norm)优点:InstanceNorm是对每个样本的每个特征进行归一化,因此它可以捕捉到更多的细节信息。InstanceNorm在某些任务,如风格迁移,中表现得很好,因为在这些任务中,细节信息很重要。缺点:InstanceNorm可能会过度强调细节信息,忽视了更宏观的信息。此外,InstanceNorm的计算成本相比Batch Norm和Layer Norm更高。

Group Normalization(Group Norm)优点:Group Norm是Batch Norm和InstanceNorm的折中方案,它在Batch的一个子集(即组)上进行归一化。这使得GroupNorm既可以捕捉到Batch的统计特性,又可以捕捉到样本的细节信息。此外,GroupNorm对Batch大小也不敏感。 缺点:GroupNorm的性能取决于组的大小,需要通过实验来确定最优的组大小。此外,GroupNorm的计算成本也比Batch Norm和Layer Norm更高。

将输入的 feature map shape记为**[N, C, H, W]**,其中N表示batchsize,即N个样本;C表示通道数;H、W分别表示特征图的高度、宽度。这几个方法主要的区别就是在:

  1. BN是在batch上,对N、H、W做归一化,而保留通道 C的维度。BN对较小的batchsize效果不好。BN适用于固定深度的前向神经网络,如CNN,不适用于RNN;
  2. LN在通道方向上,对C、H、W归一化,主要对RNN效果明显;
  3. IN在图像像素上,对H、W做归一化,用在风格化迁移;
  4. GN将channel分组,然后再做归一化。

3.6 RMS Norm

与layerNorm相比,RMSNorm的主要区别在于去掉了减去均值的部分,计算公式为:

\[\bar{a}_{i}=\frac{a_{i}}{\operatorname{RMS}(\mathbf{a})} g_{i}, \quadwhere~ \operatorname{RMS}(\mathbf{a})=\sqrt{\frac{1}{n} \sum_{i=1}^{n}a_{i}^{2}}.\]

RMS中去除了mean的统计值的使用,只使用root mean square(RMS)进行归一化。

3.7 pRMSNorm介绍

RMS具有线性特征,所以提出可以用部分数据的RMSNorm来代替全部的计算,pRMSNorm表示使用前p%的数据计算RMS值。k=n*p表示用于RMS计算的元素个数。实测中,使用6.25%的数据量可以收敛

\[\overline{\operatorname{RMS}}(\mathbf{a})=\sqrt{\frac{1}{k}\sum_{i=1}^{k} a_{i}^{2}}\]

3.8 Post-LN 和 Pre-LN

Post-LN和Pre-LN的对比图

左边是原版Transformer的Post-LN,即将LN放在addition之后;右边是改进之后的Pre-LN,即把LN放在FFN和MHA之前。

一般认为,Post-Norm在残差之后做归一化,对参数正则化的效果更强,进而模型的收敛性也会更好;而Pre-Norm有一部分参数直接加在了后面,没有对这部分参数进行正则化,可以在反向时防止梯度爆炸或者梯度消失,大模型的训练难度大,因而使用Pre-Norm较多。

目前比较明确的结论是:同一设置之下,PreNorm结构往往更容易训练,但最终效果通常不如Post Norm。PreNorm更容易训练好理解,因为它的恒等路径更突出,但为什么它效果反而没那么好呢?这个是解释的链接:https://kexue.fm/archives/9009

4 PyTorch几类优化器

4.1 梯度下降 (Gradient Descent,GD):

RMSprop (Root Mean SquarePropagation):

Adam (Adaptive MomentEstimation):

这些优化器之间的区别主要体现在对梯度的处理方式上,以及对学习率的自适应调整。例如,动量优化器可以加速收玫并帮助逃离局部最优点,而自适应学习率优化器则可以根据梯度情况动态调整学习率,适应不同参数的更新速度。

5.位置编码

5.1 绝对位置编码

不同于RNN、CNN等模型,对于Transformer模型来说,位置编码的加入是必不可少的,因为纯粹的Attention模块是无法捕捉输入顺序的,即无法区分不同位置的Token。为此我们大体有两个选择:

  1. 想办法将位置信息融入到输入中,这构成了绝对位置编码的一般做法;
  2. 想办法微调一下Attention结构,使得它有能力分辨不同位置的Token,这构成了相对位置编码的一般做法。

形式上来看,绝对位置编码是相对简单的一种方案,但即便如此,也不妨碍各路研究人员的奇思妙想,也有不少的变种。一般来说,绝对位置编码会加到输入中:在输入的第\(k\)个向量\(x_k\)中加入位置向量\(p_k\)变为\(x_k+p_k\),其中\(p_k\)只依赖于位置编号\(k\)。

5.1.1 训练式

直接将位置编码当作可训练参数,比如最大长度为512,编码维度为768,那么就初始化一个512×768的矩阵作为位置向量,让它随着训练过程更新。

对于这种训练式的绝对位置编码,一般的认为它的缺点是没有外推性,即如果预训练最大长度为512的话,那么最多就只能处理长度为512的句子,再长就处理不了了。当然,也可以将超过512的位置向量随机初始化,然后继续微调。但笔者最近的研究表明,通过层次分解的方式,可以使得绝对位置编码能外推到足够长的范围,同时保持还不错的效果,细节请参考笔者之前的博文《层次分解位置编码,让BERT可以处理超长文本》。因此,其实外推性也不是绝对位置编码的明显缺点

5.1.2 三角式

三角函数式位置编码,一般也称为Sinusoidal位置编码,是Google的论文《Attention is All YouNeed》所提出来的一个显式解:

\[\left\{\begin{array}{l}\boldsymbol{p}_{k, 2 i}=\sin \left(k / 10000^{2 i/ d}\right) \\ \boldsymbol{p}_{k, 2 i+1}=\cos \left(k / 10000^{2 i /d}\right)\end{array}\right.\]

其中\(p_{k,2i}\),\(p_{k,2i+1}\)分别是位置\(k\)的编码向量的第\(2i\),\(2i+1\)个分量,\(d\)是位置向量的维度。

很明显,三角函数式位置编码的特点是有显式的生成规律,因此可以期望于它有一定的外推性。另外一个使用它的理由是:由于\(\sin (\alpha+\beta)=\sin \alpha \cos \beta+\cos\alpha \sin \beta\)以及\(\cos(\alpha+\beta)=\cos \alpha \cos \beta-\sin \alpha \sin\beta\),这表明位置\(\alpha+\beta\)的向量可以表示成位置\(\alpha\)和位置\(\beta\)的向量组合,这提供了表达相对位置信息的可能性。但很奇怪的是,现在我们很少能看到直接使用这种形式的绝对位置编码的工作,原因不详。

5.1.3 递归式

原则上来说,RNN模型不需要位置编码,它在结构上就自带了学习到位置信息的可能性(因为递归就意味着我们可以训练一个“数数”模型),因此,如果在输入后面先接一层RNN,然后再接Transformer,那么理论上就不需要加位置编码了。同理,我们也可以用RNN模型来学习一种绝对位置编码,比如从一个向量\(p_0\)出发,通过递归格式\(p_{k+1}=f(p_k)\)来得到各个位置的编码向量。

ICML 2020的论文《Learningto Encode Position for Transformer with Continuous DynamicalModel》把这个思想推到了极致,它提出了用微分方程(ODE)\(dp_t/dt=h(p_t,t)\)的方式来建模位置编码,该方案称之为FLOATER。显然,FLOATER也属于递归模型,函数\(h(p_t,t)\)可以通过神经网络来建模,因此这种微分方程也称为神经微分方程,关于它的工作最近也逐渐多了起来。

理论上来说,基于递归模型的位置编码也具有比较好的外推性,同时它也比三角函数式的位置编码有更好的灵活性(比如容易证明三角函数式的位置编码就是FLOATER的某个特解)。但是很明显,递归形式的位置编码牺牲了一定的并行性,可能会带速度瓶颈。

5.2 相对位置编码

相对位置并没有完整建模每个输入的位置信息,而是在算Attention的时候考虑当前位置与被Attention的位置的相对距离,由于自然语言一般更依赖于相对位置,所以相对位置编码通常也有着优秀的表现。对于相对位置编码来说,它的灵活性更大,更加体现出了研究人员的“天马行空”。

5.2.1经典式

相对位置编码起源于Google的论文《Self-Attentionwith Relative PositionRepresentations》,华为开源的NEZHA模型也用到了这种位置编码,后面各种相对位置编码变体基本也是依葫芦画瓢的简单修改。

一般认为,相对位置编码是由绝对位置编码启发而来,考虑一般的带绝对位置编码的Attention:

\[\left\{\begin{aligned} \boldsymbol{q}_{i} &=\left(\boldsymbol{x}_{i}+\boldsymbol{p}_{i}\right) \boldsymbol{W}_{Q}\\ \boldsymbol{k}_{j} &=\left(\boldsymbol{x}_{j}+\boldsymbol{p}_{j}\right) \boldsymbol{W}_{K}\\ \boldsymbol{v}_{j} &=\left(\boldsymbol{x}_{j}+\boldsymbol{p}_{j}\right) \boldsymbol{W}_{V}\\ a_{i, j} & =\operatorname{softmax}\left(\boldsymbol{q}_{i}\boldsymbol{k}_{j}^{\top}\right) \\ \boldsymbol{o}_{i} & =\sum_{j}a_{i, j} \boldsymbol{v}_{j}\end{aligned}\right.\]

其中softmax对j那一维归一化,这里的向量都是指行向量。我们初步展开\(q_ik^T_j\):

\[\boldsymbol{q}_{i}\boldsymbol{k}_{j}^{\top}=\left(\boldsymbol{x}_{i}+\boldsymbol{p}_{i}\right)\boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top}\left(\boldsymbol{x}_{j}+\boldsymbol{p}_{j}\right)^{\top}=\left(\boldsymbol{x}_{i}\boldsymbol{W}_{Q}+\boldsymbol{p}_{i}\boldsymbol{W}_{Q}\right)\left(\boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{W}_{K}^{\top}\boldsymbol{p}_{j}^{\top}\right)\]

为了引入相对位置信息,Google把第一项位置去掉,第二项\(p_jW_K\)改为二元位置向量\(R^K_{i,j}\),变成

\[a_{i, j}=\operatorname{softmax}\left(\boldsymbol{x}_{i}\boldsymbol{W}_{Q}\left(\boldsymbol{x}_{j}\boldsymbol{W}_{K}+\boldsymbol{R}_{i, j}^{K}\right)^{\top}\right)\]

以及\(\boldsymbol{o}_{i}=\sum_{j} a_{i, j}\boldsymbol{v}_{j}=\sum_{j} a_{i, j}\left(\boldsymbol{x}_{j}\boldsymbol{W}_{V}+\boldsymbol{p}_{j}\boldsymbol{W}_{V}\right)\)中的中的\(p_jW_V\)换成\(R^V_{i,j}\):

\[\boldsymbol{o}_{i}=\sum_{j} a_{i, j}\left(\boldsymbol{x}_{j}\boldsymbol{W}_{V}+\boldsymbol{R}_{i, j}^{V}\right)\]

所谓相对位置,是将本来依赖于二元坐标\((i,j)\)的向量\(R^K_{i,j}\),\(R^V_{i,j}\),改为只依赖于相对距离\(i−j\),并且通常来说会进行截断,以适应不同任意的距离:

\[\begin{array}{l}\boldsymbol{R}_{i,j}^{K}=\boldsymbol{p}_{K}\left[\operatorname{clip}\left(i-j, p_{\min },p_{\max }\right)\right] \\ \boldsymbol{R}_{i,j}^{V}=\boldsymbol{p}_{V}\left[\operatorname{clip}\left(i-j, p_{\min },p_{\max }\right)\right]\end{array}\]

这样一来,只需要有限个位置编码,就可以表达出任意长度的相对位置(因为进行了截断),不管\(p_K\),\(p_V\)是选择可训练式的还是三角函数式的,都可以达到处理任意长度文本的需求。

5.2.2 XLNET式

XLNET式位置编码其实源自Transformer-XL的论文《Transformer-XL:Attentive Language Models Beyond a Fixed-LengthContext》,只不过因为使用了Transformer-XL架构的XLNET模型并在一定程度上超过了BERT后,Transformer-XL才算广为人知,因此这种位置编码通常也被冠以XLNET之名。

XLNET式位置编码源于对上述\(q_ik^T_j\)的完全展开:

\[\boldsymbol{q}_{i} \boldsymbol{k}_{j}^{\top}=\boldsymbol{x}_{i}\boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{x}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{p}_{j}^{\top}+\boldsymbol{p}_{i}\boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{p}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{p}_{j}^{\top}\]

Transformer-XL的做法很简单,直接将\(p_j\)替换为相对位置向量\(R_{i−j}\),至于两个\(p_i\),则干脆替换为两个可训练的向量\(u,v\):

\[\boldsymbol{x}_{i} \boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{x}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{R}_{i-j}^{\top}+u\boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{v} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{R}_{i-j}^{\top}\]

该编码方式中的\(R_{i−j}\)没有像经典模型那样进行截断,而是直接用了Sinusoidal式的生成方案,由于\(R_{i−j}\)的编码空间与\(x_j\)不一定相同,所以\(R_{i−j}\)前面的\(W^T_K\)换了另一个独立的矩阵\(W^T_{K,R}\),还有\(uW_Q\) 、\(vW_Q\)可以直接合并为单个\(u\) 、\(v\),所以最终使用的式子是:

\[\boldsymbol{x}_{i} \boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{x}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K, R}^{\top} \boldsymbol{R}_{i-j}^{\top}+\boldsymbol{u}\boldsymbol{W}_{K}^{\top} \boldsymbol{x}_{j}^{\top}+\boldsymbol{v}\boldsymbol{W}_{K, R}^{\top} \boldsymbol{R}_{i-j}^{\top}\]

此外,\(v_j\)上的位置偏置就直接去掉了,即直接令\(\boldsymbol{o}_{i}=\sum_{j} a_{i, j}\boldsymbol{x}_{j}\boldsymbol{W}_{V}\)。似乎从这个工作开始,后面的相对位置编码都只加到Attention矩阵上去,而不加到\(v_j\)上去了。

5.2.3 T5式

T5模型出自文章《Exploringthe Limits of Transfer Learning with a Unified Text-to-TextTransformer》,里边用到了一种更简单的相对位置编码。思路依然源自\(q_ik^T_j\)展开式,如果非要分析每一项的含义,那么可以分别理解为“输入-输入”、“输入-位置”、“位置-输入”、“位置-位置”四项注意力的组合。如果我们认为输入信息与位置信息应该是独立(解耦)的,那么它们就不应该有过多的交互,所以“输入-位置”、“位置-输入”两项Attention可以删掉,而\(\boldsymbol{p}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top}\boldsymbol{p}_{j}^{\top}\)实际上只是一个只依赖于\((i,j)\)的标量,我们可以直接将它作为参数训练出来,即简化为:

\[\boldsymbol{x}_{i} \boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{\beta}_{i, j}\]

说白了,它仅仅是在Attention矩阵的基础上加一个可训练的偏置项而已,而跟XLNET式一样,在\(v_j\)上的位置偏置则直接被去掉了。包含同样的思想的还有微软在ICLR2021的论文《RethinkingPositional Encoding in LanguagePre-training》中提出的TUPE位置编码。

比较“别致”的是,不同于常规位置编码对将\(\beta_{i, j}\)视为\(i−j\)的函数并进行截断的做法,T5对相对位置进行了一个“分桶”处理,即相对位置是\(i−j\)的位置实际上对应的是\(f(i−j)\)位置,映射关系如下:

\(i-j\)0123456789101112131415
\(f(i-j)\)0123456788889999
\(i-j\)161718192021222324252627282930...
\(f(i-j)\)101010101010101111111111111111...

这个设计的思路其实也很直观,就是比较邻近的位置(0~7),需要比较得精细一些,所以给它们都分配一个独立的位置编码,至于稍远的位置(比如8~11),我们不用区分得太清楚,所以它们可以共用一个位置编码,距离越远,共用的范围就可以越大,直到达到指定范围再clip。

5.2.4 DeBERTa式

DeBERTa也是微软搞的,去年6月就发出来了,论文为《DeBERTa:Decoding-enhanced BERT with DisentangledAttention》,最近又小小地火了一把,一是因为它正式中了ICLR2021,二则是它登上SuperGLUE的榜首,成绩稍微超过了T5。

其实DeBERTa的主要改进也是在位置编码上,同样还是从\(q_ik^T_j\)展开式出发,T5是干脆去掉了第2、3项,只保留第4项并替换为相对位置编码,而DeBERTa则刚刚相反,它扔掉了第4项,保留第2、3项并且替换为相对位置编码(果然,科研就是枚举所有的排列组合看哪个最优):

\[\boldsymbol{q}_{i} \boldsymbol{k}_{j}^{\top}=\boldsymbol{x}_{i}\boldsymbol{W}_{Q} \boldsymbol{W}_{K}^{\top}\boldsymbol{x}_{j}^{\top}+\boldsymbol{x}_{i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{R}_{i,j}^{\top}+\boldsymbol{R}_{j, i} \boldsymbol{W}_{Q}\boldsymbol{W}_{K}^{\top} \boldsymbol{x}_{j}^{\top}\]

不过,DeBERTa比较有意思的地方,是提供了使用相对位置和绝对位置编码的一个新视角,它指出NLP的大多数任务可能都只需要相对位置信息,但确实有些场景下绝对位置信息更有帮助,于是它将整个模型分为两部分来理解。以Base版的MLM预训练模型为例,它一共有13层,前11层只是用相对位置编码,这部分称为Encoder,后面2层加入绝对位置信息,这部分它称之为Decoder,还弄了个简称EMD(EnhancedMaskDecoder);至于下游任务的微调截断,则是使用前11层的Encoder加上1层的Decoder来进行。

5.3 旋转位置编码 RoPE篇

RoPE旋转位置编码是苏神提出来的一种相对位置编码,之前主要用在自研的语言模型roformer上,后续谷歌Palm和meta的LLaMA等都是采用此位置编码,通过复数形式来对于三角式绝对位置编码的改进。有一些同学可能没看懂苏神的公式推导,我这里来帮助大家推理理解下公式。

通过线性attention演算,现在q和k向量中引入绝对位置信息:

\[\tilde{\boldsymbol{q}}_{m}=\boldsymbol{f}(\boldsymbol{q}, m), \quad\tilde{\boldsymbol{k}}_{n}=\boldsymbol{f}(\boldsymbol{k}, n)\]

但是需要实现相对位置编码的话,需要显式融入相对。attention运算中q和k会进行内积,所以考虑在进行向量内积时考虑融入相对位置。所以假设成立恒等式:

\[\langle\boldsymbol{f}(\boldsymbol{q}, m), \boldsymbol{f}(\boldsymbol{k},n)\rangle=g(\boldsymbol{q}, \boldsymbol{k}, m-n)\]

其中m-n包含着token之间的相对位置信息。

给上述恒等式计算设置初始条件,例如\(f(q,0)=q\),\(f(k,0)=k\)。

求解过程使用复数方式求解

将内积使用复数形式表示:

\[\langle\boldsymbol{q},\boldsymbol{k}\rangle=\operatorname{Re}\left[\boldsymbol{q}\boldsymbol{k}^{*}\right]\]

转化上面内积公式可得:

\[\operatorname{Re}\left[\boldsymbol{f}(\boldsymbol{q}, m)\boldsymbol{f}^{*}(\boldsymbol{k}, n)\right]=g(\boldsymbol{q},\boldsymbol{k}, m-n)\]

假设等式两边都存在复数形式,则有下式:

\[\boldsymbol{f}(\boldsymbol{q}, m) \boldsymbol{f}^{*}(\boldsymbol{k},n)=\boldsymbol{g}(\boldsymbol{q}, \boldsymbol{k}, m-n)\]

将两边公式皆用复数指数形式表示:

存在\(r e^{\theta \mathrm{j}}=r \cos\theta+r \sin \theta \mathrm{j}\),即任意复数\(z\)可以表示为\(\boldsymbol{z}=r e^{\theta\mathrm{j}}\),其中\(r\)为复数的模,\(\theta\)为幅角。

\[\begin{aligned} \boldsymbol{f}(\boldsymbol{q}, m) &=R_{f}(\boldsymbol{q}, m) e^{\mathrm{i} \Theta_{f}(\boldsymbol{q}, m)}\\ \boldsymbol{f}(\boldsymbol{k}, n) & =R_{f}(\boldsymbol{k}, n)e^{\mathrm{i} \Theta_{f}(\boldsymbol{k}, n)} \\\boldsymbol{g}(\boldsymbol{q}, \boldsymbol{k}, m-n) &=R_{g}(\boldsymbol{q}, \boldsymbol{k}, m-n) e^{\mathrm{i}\Theta_{g}(\boldsymbol{q}, \boldsymbol{k}, m-n)}\end{aligned}\]

由于带入上面方程中\(f(k,n)\)带*是共轭复数,所以指数形式应该是\(e^{-x}\)形式,带入上式公式可得方程组:

\[\begin{aligned} R_{f}(\boldsymbol{q}, m) R_{f}(\boldsymbol{k}, n) &=R_{g}(\boldsymbol{q}, \boldsymbol{k}, m-n) \\\Theta_{f}(\boldsymbol{q}, m)-\Theta_{f}(\boldsymbol{k}, n) &=\Theta_{g}(\boldsymbol{q}, \boldsymbol{k}, m-n)\end{aligned}\]

第一个方程带入条件\(m=n\)化简可得:

\[R_{f}(\boldsymbol{q}, m) R_{f}(\boldsymbol{k}, m)=R_{g}(\boldsymbol{q},\boldsymbol{k}, 0)=R_{f}(\boldsymbol{q}, 0) R_{f}(\boldsymbol{k},0)=\|\boldsymbol{q}\|\|\boldsymbol{k}\|\]

\[R_{f}(\boldsymbol{q}, m)=\|\boldsymbol{q}\|, R_{f}(\boldsymbol{k},m)=\|\boldsymbol{k}\|\]

从上式可以看出来复数\(f(q,m)\)和\(f(k,m)\)与\(m\)取值关系不大。

第二个方程带入\(m=n\)化简可得:

\[\Theta_{f}(\boldsymbol{q}, m)-\Theta_{f}(\boldsymbol{k},m)=\Theta_{g}(\boldsymbol{q}, \boldsymbol{k},0)=\Theta_{f}(\boldsymbol{q}, 0)-\Theta_{f}(\boldsymbol{k},0)=\Theta(\boldsymbol{q})-\Theta(\boldsymbol{k})\]

上式公式变量两边挪动下得到:

\[\Theta_{f}(\boldsymbol{q}, m)-\Theta_{f}(\boldsymbol{k},m)=\Theta_{g}(\boldsymbol{q}, \boldsymbol{k},0)=\Theta_{f}(\boldsymbol{q}, 0)-\Theta_{f}(\boldsymbol{k},0)=\Theta(\boldsymbol{q})-\Theta(\boldsymbol{k})\]

其中上式结果相当于m是自变量,结果是与m相关的值,假设为 \(\varphi(m)\),即\(\Theta_{f}(\boldsymbol{q},m)=\Theta(\boldsymbol{q})+\varphi(m)\)

n假设为m的前一个token,则可得n=m-1,带入上上个式子可得:

\[\varphi(m)-\varphi(m-1)=\Theta_{g}(\boldsymbol{q}, \boldsymbol{k},1)+\Theta(\boldsymbol{k})-\Theta(\boldsymbol{q})\]

\(\varphi(m)\)是等差数列,假设等式右边为\(\theta\),则mm-1位置的公差就是为\(\theta\),可推得 \(\varphi(m)=m \theta\)。

得到二维情况下用复数表示的RoPE:

\[\boldsymbol{f}(\boldsymbol{q}, m)=R_{f}(\boldsymbol{q}, m) e^{\mathrm{i}\Theta_{f}(\boldsymbol{q}, m)}=\|q\|e^{\mathrm{i}(\Theta(\boldsymbol{q})+m \theta)}=\boldsymbol{q}e^{\mathrm{i} m \theta}\]

矩阵形式是:

\[\boldsymbol{f}(\boldsymbol{q}, m)=\left(\begin{array}{cc}\cos m \theta& -\sin m \theta \\ \sin m \theta & \cos m\theta\end{array}\right)\left(\begin{array}{l}q_{0} \\q_{1}\end{array}\right)\]

公式最后还会采用三角式一样的远程衰减,来增加周期性函数外推位置差异性。

\[\left(\boldsymbol{W}_{m}\boldsymbol{q}\right)^{\top}\left(\boldsymbol{W}_{n}\boldsymbol{k}\right)=\operatorname{Re}\left[\sum_{i=0}^{d / 2-1}\boldsymbol{q}_{[2 i: 2 i+1]} \boldsymbol{k}_{[2 i: 2 i+1]}^{*}e^{\mathrm{i}(m-n) \theta_{i}}\right]\]

5.4 几种位置编码方式总结

5.4.1 绝对位置编码

5.4.2 相对位置编码

5.4.3 RoPE

6 激活函数

6.1 介绍一下 FFN 块 计算公式?

FFN(Feed-ForwardNetwork)块是Transformer模型中的一个重要组成部分,接受自注意力子层的输出作为输入,并通过一个带有Relu激活函数的两层全连接网络对输入进行更加复杂的非线性变换。实验证明,这一非线性变换会对模型最终的性能产生十分重要的影响。

FFN由两个全连接层(即前馈神经网络)和一个激活函数组成。下面是FFN块的计算公式:

\[\operatorname{FFN}(\boldsymbol{x})=\operatorname{Relu}\left(\boldsymbol{x}\boldsymbol{W}_{1}+\boldsymbol{b}_{1}\right)\boldsymbol{W}_{2}+\boldsymbol{b}_{2}\]

假设输入是一个向量 \(x\),FFN块的计算过程如下:

  1. 第一层全连接层(线性变换):\(z = xW1 +b1\) 其中,W1 是第一层全连接层的权重矩阵,b1 是偏置向量。
  2. 激活函数:\(a = g(z)\) 其中,g()是激活函数,常用的激活函数有ReLU(Rectified Linear Unit)等。
  3. 第二层全连接层(线性变换):\(y = aW2 +b2\) 其中,W2 是第二层全连接层的权重矩阵,b2 是偏置向量。

增大前馈子层隐状态的维度有利于提升最终翻译结果的质量,因此,前馈子层隐状态的维度一般比自注意力子层要大。

需要注意的是,上述公式中的 W1、b1、W2、b2是FFN块的可学习参数,它们会通过训练过程进行学习和更新。

6.2 介绍一下 GeLU 计算公式?

GeLU(Gaussian Error LinearUnit)是一种激活函数,常用于神经网络中的非线性变换。它在Transformer模型中广泛应用于FFN(Feed-ForwardNetwork)块。下面是GeLU的计算公式:

假设输入是一个标量 x,GeLU的计算公式如下:

\[GeLU(x) = 0.5 \times x \times (1 + tanh(\sqrt{\frac{2}{\pi}} \times (x +0.044715 \times x^3)))\]

其中,tanh()是双曲正切函数,sqrt()是平方根函数,$ $是圆周率。

1
2
3
4
import numpy as np

def GELU(x):
return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * np.power(x, 3))))

相对于 Sigmoid 和 Tanh 激活函数,ReLU 和 GeLU更为准确和高效,因为它们在神经网络中的梯度消失问题上表现更好。而 ReLU 和GeLU几乎没有梯度消失的现象,可以更好地支持深层神经网络的训练和优化。

ReLU 和 GeLU 的区别在于形状和计算效率。ReLU是一个非常简单的函数,仅仅是输入为负数时返回0,而输入为正数时返回自身,从而仅包含了一次分段线性变换。但是,ReLU函数存在一个问题,就是在输入为负数时,输出恒为0,这个问题可能会导致神经元死亡,从而降低模型的表达能力。GeLU函数则是一个连续的 S 形曲线,介于 Sigmoid 和 ReLU 之间,形状比 ReLU更为平滑,可以在一定程度上缓解神经元死亡的问题。不过,由于 GeLU函数中包含了指数运算等复杂计算,所以在实际应用中通常比 ReLU 慢。

总之,ReLU 和 GeLU都是常用的激活函数,它们各有优缺点,并适用于不同类型的神经网络和机器学习问题。一般来说,ReLU更适合使用在卷积神经网络(CNN)中,而 GeLU更适用于全连接网络(FNN)。

6.3 介绍一下 Swish 计算公式?

Swish是一种激活函数,它在深度学习中常用于神经网

络的非线性变换。Swish函数的计算公式如下: \[Swish(x) = x \times sigmoid(\beta * x)\]

其中,\(sigmoid()\)是Sigmoid函数,\(x\) 是输入,\(\beta\) 是一个可调节的超参数。

Swish函数的特点是在接近零的区域表现得类似于线性函数,而在远离零的区域则表现出非线性的特性。相比于其他常用的激活函数(如ReLU、tanh等),Swish函数在某些情况下能够提供更好的性能和更快的收敛速度。

Swish函数的设计灵感来自于自动搜索算法,它通过引入一个可调节的超参数来增加非线性程度。当beta为0时,Swish函数退化为线性函数;当beta趋近于无穷大时,Swish函数趋近于ReLU函数。

需要注意的是,Swish函数相对于其他激活函数来说计算开销较大,因为它需要进行Sigmoid运算。因此,在实际应用中,也可以根据具体情况选择其他的激活函数来代替Swish函数。

6.4介绍一下使用 GLU 线性门控单元的 FFN 块 计算公式?

使用GLU(Gated Linear Unit)线性门控单元的FFN(Feed-ForwardNetwork)块是Transformer模型中常用的结构之一。它通过引入门控机制来增强模型的非线性能力。下面是使用GLU线性门控单元的FFN块的计算公式:

假设输入是一个向量 x,GLU线性门控单元的计算公式如下:

\[GLU(x) = x * sigmoid(W_1 * x)\]

其中,\(sigmoid()\)是Sigmoid函数,\(W_1\)是一个可学习的权重矩阵。

在公式中,首先将输入向量 x 通过一个全连接层(线性变换)得到一个与 x维度相同的向量,然后将该向量通过Sigmoid函数进行激活。这个Sigmoid函数的输出称为门控向量,用来控制输入向量x 的元素是否被激活。最后,将门控向量与输入向量 x逐元素相乘,得到最终的输出向量。

GLU线性门控单元的特点是能够对输入向量进行选择性地激活,从而增强模型的表达能力。它在Transformer模型的编码器和解码器中广泛应用,用于对输入向量进行非线性变换和特征提取。

需要注意的是,GLU线性门控单元的计算复杂度较高,可能会增加模型的计算开销。因此,在实际应用中,也可以根据具体情况选择其他的非线性变换方式来代替GLU线性门控单元。

6.5 介绍一下 使用 GeLU 的GLU 块 计算公式?

使用GeLU作为激活函数的GLU块的计算公式如下:

\[GLU(x) = x * GeLU(W_1 * x)\]

其中,GeLU()是Gaussian Error LinearUnit的激活函数,W_1是一个可学习的权重矩阵。

在公式中,首先将输入向量 x 通过一个全连接层(线性变换)得到一个与 x维度相同的向量,然后将该向量作为输入传递给GeLU激活函数进行非线性变换。最后,将GeLU激活函数的输出与输入向量x 逐元素相乘,得到最终的输出向量。

GeLU激活函数的计算公式如下:

\[GeLU(x) = 0.5 \times x \times (1 + tanh(\sqrt{\frac{2}{\pi}} \times (x +0.044715 \times x^3)))\]

其中,tanh()是双曲正切函数,sqrt()是平方根函数,$ $是圆周率。

在公式中,GeLU函数首先对输入向量 x进行一个非线性变换,然后通过一系列的数学运算得到最终的输出值。

使用GeLU作为GLU块的激活函数可以增强模型的非线性能力,并在某些情况下提供更好的性能和更快的收敛速度。这种结构常用于Transformer模型中的编码器和解码器,用于对输入向量进行非线性变换和特征提取。

需要注意的是,GLU块和GeLU激活函数是两个不同的概念,它们在计算公式和应用场景上有所区别。在实际应用中,可以根据具体情况选择合适的激活函数来代替GeLU或GLU。

6.6 介绍一下 使用 Swish的 GLU 块 计算公式?

使用Swish作为激活函数的GLU块的计算公式如下:

\[GLU(x) = x * sigmoid(W_1 * x)\]

其中,\(sigmoid()\)是Sigmoid函数,\(W_1\)是一个可学习的权重矩阵。

在公式中,首先将输入向量 x 通过一个全连接层(线性变换)得到一个与 x维度相同的向量,然后将该向量通过Sigmoid函数进行激活。这个Sigmoid函数的输出称为门控向量,用来控制输入向量x 的元素是否被激活。最后,将门控向量与输入向量 x逐元素相乘,得到最终的输出向量。

Swish激活函数的计算公式如下:

\[Swish(x) = x \times sigmoid(\beta * x)\]

其中,\(sigmoid()\)是Sigmoid函数,\(x\) 是输入,\(\beta\) 是一个可调节的超参数。

在公式中,Swish函数首先对输入向量 x进行一个非线性变换,然后通过Sigmoid函数进行激活,并将该激活结果与输入向量x 逐元素相乘,得到最终的输出值。

使用Swish作为GLU块的激活函数可以增强模型的非线性能力,并在某些情况下提供更好的性能和更快的收敛速度。GLU块常用于Transformer模型中的编码器和解码器,用于对输入向量进行非线性变换和特征提取。

需要注意的是,GLU块和Swish激活函数是两个不同的概念,它们在计算公式和应用场景上有所区别。在实际应用中,可以根据具体情况选择合适的激活函数来代替Swish或GLU。

7 Tokenize相关

7.1 总览

分词方法特点被提出的时间典型模型
BPE采用合并规则,可以适应未知词2016年GPT-2、RoBERTa
WordPiece采用逐步拆分的方法,可以适应未知词2016年BERT
Unigram LM采用无序语言模型,训练速度快2018年XLM
SentencePiece采用汉字、字符和子词三种分词方式,支持多语言2018年T5、ALBERT

背景与基础

在使用GPT BERT模型输入词语常常会先进行tokenize,tokenize的目标是把输入的文本流,切分成一个个子串,每个子串相对有完整的语义,便于学习embedding表达和后续模型的使用。

tokenize有三种粒度:word/subword/char

7.2 常用的tokenize算法

最常用的三种tokenize算法:BPE(Byte-PairEncoding),WordPiece和SentencePiece

7.2.1 BPE(Byte-Pair Encoding)

BPE,即字节对编码。其核心思想在于将最常出现的子词对合并,直到词汇表达到预定的大小时停止

BPE是一种基于数据压缩算法的分词方法。它通过不断地合并出现频率最高的字符或者字符组合,来构建一个词表。具体来说,BPE的运算过程如下:

  1. 将所有单词按照字符分解为字母序列。例如:“hello”会被分解为["h","e","l","l","o"]。
  2. 统计每个字母序列出现的频率,将频率最高的序列合并为一个新序列。
  3. 重复第二步,直到达到预定的词表大小或者无法再合并。

词表大小通常先增加后减小

每次合并后词表可能出现3种变化:

7.2.2 WordPiece

WordPiece,从名字好理解,它是一种子词粒度的tokenize算法subwordtokenizationalgorithm,很多著名的Transformers模型,比如BERT/DistilBERT/Electra都使用了它。

wordpiece算法可以看作是BPE的变种。不同的是,WordPiece基于概率生成新的subword而不是下一最高频字节对。WordPiece算法也是每次从词表中选出两个子词合并成新的子词。BPE选择频数最高的相邻子词合并,而WordPiece选择使得语言模型概率最大的相邻子词加入词表。即它每次合并的两个字符串A和B,应该具有最大的\(\frac{P(A B)}{P(A)P(B)}\)值。合并AB之后,所有原来切成A+B两个tokens的就只保留AB一个token,整个训练集上最大似然变化量与\(\frac{P(A B)}{P(A) P(B)}\)成正比。

\[\log P(S)=\sum_{i=1}^{n} \log P\left(t_{i}\right)\]

\[S=\left[t_{1}, t_{2}, t_{3}, \ldots, t_{n}\right]\]

比如说 $ P(ed) \(的概率比\)P(e) +P(d)$单独出现的概率更大,可能比他们具有最大的互信息值,也就是两子词在语言模型上具有较强的关联性。

那wordPiece和BPE的区别:

7.2.3 Unigram

与BPE或者WordPiece不同,Unigram的算法思想是从一个巨大的词汇表出发,再逐渐删除trimdown其中的词汇,直到size满足预定义。

初始的词汇表可以采用所有预分词器分出来的词,再加上所有高频的子串

每次从词汇表中删除词汇的原则是使预定义的损失最小。训练时,计算loss的公式为:

\[Loss =-\sum_{i=1}^{N} \log \left(\sum_{x \in S\left(x_{i}\right)}p(x)\right)\]

假设训练文档中的所有词分别为\(x_{1} ;x_{2}, \ldots,x_{N}\),而每个词tokenize的方法是一个集合\(S\left(x_{i}\right)\)

当一个词汇表确定时,每个词tokenize的方法集合\(S\left(x_{i}\right)\)就是确定的,而每种方法对应着一个概率\(P(x)\).

如果从词汇表中删除部分词,则某些词的tokenize的种类集合就会变少,log(*)中的求和项就会减少,从而增加整体loss。

Unigram算法每次会从词汇表中挑出使得loss增长最小的10%~20%的词汇来删除。

一般Unigram算法会与SentencePiece算法连用。

7.2.4 SentencePiece

SentencePiece,顾名思义,它是把一个句子看作一个整体,再拆成片段,而没有保留天然的词语的概念。一般地,它把空格space也当作一种特殊字符来处理,再用BPE或者Unigram算法来构造词汇表

比如,XLNetTokenizer就采用了_来代替空格,解码的时候会再用空格替换回来。

目前,Tokenizers库中,所有使用了SentencePiece的都是与Unigram算法联合使用的,比如ALBERT、XLNet、Marian和T5.

8 长度外推问题篇

8.1 长度外推问题

大模型的外推性问题是指大模型在训练时和预测时的输入长度不一致,导致模型的泛化能力下降的问题。在目前的大模型中,一般指的是超出预训练设置的上下文长度时,依旧保持良好推理效果的能力。

长度外推性=train short, test long

trainshort:1)受限于训练成本;2)大部分文本的长度不会特别长,训练时的max_length特别特别大其实意义不大(长尾)。

testlong:这里long是指比训练时的max_length长,希望不用微调就能在长文本上也有不错的效果。

8.2长度外推问题的解决方法有哪些?

(1)进制表示

我们将整数n以一个三维向量[a,b,c]来输入,a,b,c分别是n的百位、十位、个位。这样,我们既缩小了数字的跨度,又没有缩小相邻数字的差距,代价了增加了输入的维度——刚好,神经网络擅长处理高维数据。

如果想要进一步缩小数字的跨度,我们还可以进一步缩小进制的基数,如使用8进制、6进制甚至2进制,代价是进一步增加输入的维度。

(2)直接外推

简单来说,假如原来位置编码用三维向量表示,那外插就是直接增加一维。

可以提前预留多几维,训练阶段设为0,推理阶段直接改为其他数字,这就是外推(Extrapolation)。

长度外推问题的示意图

然而,训练阶段预留的维度一直是0,如果推理阶段改为其他数字,效果不见得会好,因为模型对没被训练过的情况不一定具有适应能力。也就是说,由于某些维度的训练数据不充分,所以直接进行外推通常会导致模型的性能严重下降

(3)线性插值

就是将2000以内压缩到1000以内,比如通过除以2,1749就变成了874.5,然后转为三维向量[8,7,4.5]输入到原来的模型中。从绝对数值来看,新的[7,4,9]实际上对应的是1498,是原本对应的2倍,映射方式不一致;从相对数值来看,原本相邻数字的差距为1,现在是0.5,最后一个维度更加“拥挤”。所以,做了内插修改后,通常都需要微调训练,以便模型重新适应拥挤的映射关系。

线性插值的表示

(4)进制转换

有没有不用新增维度,又能保持相邻差距的方案呢?进制转换!三个数字的10进制编码可以表示0~999,如果是16进制呢?它最大可以表示163−1=4095>1999。所以,只需要转到16进制,如1749变为[6,13,5],那么三维向量就可以覆盖目标范围,代价是每个维度的数字从0~9变为0~15。

其他文章参考

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

LLAMA2结构https://blog.csdn.net/sikh_0529/article/details/134375318

旋转位置嵌入https://www.zhihu.com/tardis/zm/art/647109286?source_id=1005

bert模型细节 https://www.zhihu.com/question/534763354

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

Qlora https://zhuanlan.zhihu.com/p/618894919

RLHF https://zhuanlan.zhihu.com/p/631238431

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

fine-tuningllama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

fine-tuning Llama 2 with PEFT'sQLoRa[https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/](

基础代码

PyTorch实现基础网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

Batch Norm代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta

PyTorch实现Attention

自注意力机制的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

多头注意力机制的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)

PyTorch实现DQN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

PyTorch实现PPO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

]]> @@ -138,33 +138,6 @@ - - 【深度学习】DeepL知识汇总 - - /2024/01/30/dl_summary/ - - 深度学习知识汇总

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

  • https://zhuanlan.zhihu.com/p/560482252
  • https://blog.csdn.net/weixin_42693876/article/details/120345924
  • https://zhuanlan.zhihu.com/p/667048896

训练与模型

  • L1L2范数 https://blog.csdn.net/weixin_35849560/article/details/113395018

  • L2范数https://blog.csdn.net/u010725283/article/details/79212762

逻辑回归和线性回归

线性回归解决的是回归问题,逻辑回归相当于是线性回归的基础上,来解决分类问题

线性回归(Linear Regression) \[\begin{aligned}&f_{w, b}(x)=\sum_i w_i x_i+b\\\end{aligned}\] 逻辑回归(Logistic Regression) $$\[\begin{aligned}&f_{w, b}(x)=\sigma\left(\sum_i w_i x_i+b\right)\end{aligned}\]

$$ 逻辑回归可以理解为在线性回归后加了一个 sigmoid函数。将线性回归变成一个0~1输出的分类问题。逻辑回归本质上是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归的。可以说,逻辑回归都是以线性回归为理论支持的,只不过逻辑回归可以轻松解决0/1 分类问题。

深度学习模型的参数范围

因为参数越小代表模型越简单,越是复杂的模型,越是尝试对所有样本进行拟合,包括异常点。这就会造成在较小的区间中产生较大的波动,这个较大的波动也会反映在这个区间的导数比较大。只有越大的参数才可能产生较大的导数。因此参数越小,模型就越简单。

实现参数稀疏

参数的稀疏,在一定程度上实现了特征的选择。一般而言,大部分特征对模型是没有贡献的。这些没有用的特征虽然可以减少训练集上的误差,但是对测试集的样本,反而会产生干扰。稀疏参数的引入,可以将那些无用的特征的权重置为0

Batchsize的大小对学习率的影响

  • batch-size大,学习率也可以取得大一点,而且,batch-size大通常更新次数少,所以需要更多的epoch才能让loss收敛。
  • batch-size小,学习率应该取得小一点,取的大会发生nan(梯度爆炸了),batch-size小通常更新次数多,较少的epoch就课可以让loss收敛,但是缺点是训练过程慢。

为什么batch-size小,学习率取的大会发生nan?学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)

优化器于损失函数

优化器optimizer和损失函数loss function的区别:

  1. 优化器定义了哪些参数是要用来更新的,并且设置了更新的方式(学习率、动量、SGD等),还有一些权重衰减的设置。
  2. 损失函数是用来计算损失的,也可以说损失函数是负责反向传播求导用的

训练中loss快速增大

训练过程中发现loss快速增大应该从哪些方面考虑?

  1. 学习率过大,导致学习的过程非常不平稳导致的损失值快速增大
  2. 训练的样本中存在坏数据,造成了数据的污染

梯度消失和梯度爆炸

梯度消失的原因和解决

  1. 隐藏层的层数过多

    反向传播求梯度时的链式求导法则,某部分梯度小于1,则多层连乘后出现梯度消失

  2. 采用了不合适的激活函数

    如sigmoid函数的最大梯度为1/4,这意味着隐藏层每一层的梯度均小于1(权值小于1时),出现梯度消失

  3. 解决方法:1、relu激活函数,使导数衡为1 2、batch norm3、残差结构

梯度爆炸的原因和解决办法

  1. 隐藏层的层数过多,某部分梯度大于1,则多层连乘后,梯度呈指数增长,产生梯度爆炸。
  2. 权重初始值太大,求导时会乘上权重
  3. 解决方法:1、梯度裁剪 2、权重L1/L2正则化 3、残差结构 4、batchnorm

如何取消张量梯度

可以选择使用torch.no_grad来将某个张量的值取消计算梯度,model.evalvs和torch.no_grad区别

  • model.eval():依然计算梯度,但是不反传;dropout层保留概率为1;batchnorm层使用全局的mean和var
  • with torch.no_grad: 不计算梯度

Dropout和BatchNorm

Dropout和Batch norm能否一起使用?

可以,但是只能将Dropout放在Batchnorm之后使用。因为Dropout训练时会改变输入X的方差,从而影响Batchnorm训练过程中统计的滑动方差值;而测试时没有Dropout,输入X的方差和训练时不一致,这就导致Batchnorm测试时期望的方差和训练时统计的有偏差。

PyTorch实现基础网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

PyTorch几类优化器

  • 几类优化器的区分https://zhuanlan.zhihu.com/p/78622301
  • 深度学习基础https://zhuanlan.zhihu.com/p/560482252

Adam与SGD的区别

SGD缺点是其更新方向完全依赖于当前batch计算出的梯度,因而十分不稳定。

Adam的优点主要在于:

  • 考虑历史步中的梯度更新信息,能够降低梯度更新噪声。
  • 此外经过偏差校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。

但是Adam也有其自身问题:可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。二者似乎都没法很好避免局部最优问题。

Norm归一化

  • LayerNorm https://blog.csdn.net/weixin_45069761/article/details/107834049
  • BatchNorm https://zhuanlan.zhihu.com/p/93643523
  • Batch和miniBatch https://link.zhihu.com/?target=https%3A//blog.csdn.net/xys430381_1/article/details/80680167

Batch Norm代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta

Transformer部分

  • Transformer里的LN https://blog.csdn.net/weixin_45069761/article/details/107834049
  • Transformer为什么用多头 https://www.zhihu.com/question/341222779

残差结构的设计

残差结构设计思想:残差网络的本质也是解决梯度消失/爆炸的问题,只不过是在网络结构层面的改变残差网络的出现解决了构建深层神经网络时网络退化即梯度消失/爆炸的问题。残差结构主要设计有两个,快捷连接(shortcutconnection)和恒等映射(identitymapping),快捷连接使得残差变得可能,而恒等映射使得网络变深,恒等映射主要有两个:跳跃连接和激活函数

PyTorch实现Attention

自注意力机制的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

多头注意力机制的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)

PyTorch实现Transformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import torch
import torch.nn as nn
import torch.nn.functional as F

class MultiHeadAttention(nn.Module):
def __init__(self, embed_dim, num_heads):
super(MultiHeadAttention, self).__init__()
self.num_heads = num_heads
self.head_dim = embed_dim // num_heads

self.query_fc = nn.Linear(embed_dim, embed_dim)
self.key_fc = nn.Linear(embed_dim, embed_dim)
self.value_fc = nn.Linear(embed_dim, embed_dim)
self.fc_out = nn.Linear(embed_dim, embed_dim)

def forward(self, query, key, value, mask=None):
batch_size = query.shape[0]

# Linearly project queries, keys, and values
Q = self.query_fc(query)
K = self.key_fc(key)
V = self.value_fc(value)

# Split the embedding into num_heads
Q = Q.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)

# Calculate the attention scores
scores = torch.matmul(Q, K.permute(0, 1, 3, 2)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))

if mask is not None:
scores = scores.masked_fill(mask == 0, float("-1e20"))

# Apply softmax to get attention probabilities
attention_weights = F.softmax(scores, dim=-1)

# Apply dropout
attention_weights = F.dropout(attention_weights, p=0.1, training=self.training)

# Multiply the attention weights with the values
output = torch.matmul(attention_weights, V)

# Concatenate multi-heads and project
output = output.permute(0, 2, 1, 3).contiguous().view(batch_size, -1, embed_dim)
output = self.fc_out(output)

return output, attention_weights

class PositionwiseFeedforward(nn.Module):
def __init__(self, embed_dim, hidden_dim):
super(PositionwiseFeedforward, self).__init__()
self.fc1 = nn.Linear(embed_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, embed_dim)

def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x

class EncoderLayer(nn.Module):
def __init__(self, embed_dim, num_heads, hidden_dim):
super(EncoderLayer, self).__init__()
self.multihead_attention = MultiHeadAttention(embed_dim, num_heads)
self.feed_forward = PositionwiseFeedforward(embed_dim, hidden_dim)
self.layer_norm1 = nn.LayerNorm(embed_dim)
self.layer_norm2 = nn.LayerNorm(embed_dim)

def forward(self, x, mask=None):
# Multi-Head Attention
residual = x
x, _ = self.multihead_attention(x, x, x, mask)
x = self.layer_norm1(x + residual)

# Feed Forward
residual = x
x = self.feed_forward(x)
x = self.layer_norm2(x + residual)

return x

class TransformerEncoder(nn.Module):
def __init__(self, vocab_size, embed_dim, num_layers, num_heads, hidden_dim):
super(TransformerEncoder, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.layers = nn.ModuleList([EncoderLayer(embed_dim, num_heads, hidden_dim) for _ in range(num_layers)])

def forward(self, x, mask=None):
x = self.embedding(x)
for layer in self.layers:
x = layer(x, mask)
return x

强化学习部分

PyTorch实现DQN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

PyTorch实现PPO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

自动驾驶和RL联系与设计

设计和实现一个基于强化学习的自动驾驶决策规划系统是一个复杂而且需要深入思考的任务。下面是一个基本的设计和实现方案:

设计方案
  1. 环境建模:首先,需要对自动驾驶的环境进行建模,包括车辆、道路、交通规则、障碍物、目标等。环境可以使用基于物理的仿真环境,例如CARLA,或者基于虚拟仿真的环境。
  2. 状态空间定义:定义自动驾驶车辆的状态空间,这可能包括车辆的位置、速度、方向、周围车辆的位置和速度等信息。
  3. 动作空间定义:定义自动驾驶车辆可以执行的动作空间,如加速、减速、转向等。
  4. 奖励函数设计:设计奖励函数来评估每个状态下执行的动作,以指导智能体的学习。奖励函数应该考虑安全性、效率、舒适性等因素。
  5. 智能体模型选择:选择合适的强化学习算法和模型架构来训练智能体,例如深度Q网络(DQN)、深度确定性策略梯度(DDPG)、双重深度确定性策略梯度(TD3)等。
  6. 训练策略:定义训练策略,包括学习率、优化器、探索策略等。
  7. 评估与测试:在仿真环境中对训练好的智能体进行评估和测试,检查其性能和鲁棒性。
实现方案
  1. 环境建模: 使用CARLA等仿真平台进行环境建模。
  2. 状态空间和动作空间定义:编写代码从环境中获取车辆状态信息,并定义可以执行的动作。
  3. 奖励函数设计:根据项目需求和目标设计奖励函数。
  4. 智能体模型选择:根据任务选择合适的强化学习算法和模型架构,例如使用深度神经网络作为Q值函数的估计器。
  5. 训练智能体:使用收集的数据对智能体进行训练,调整网络参数,以最大化奖励函数。
  6. 评估与测试:在仿真环境中评估训练后的智能体的性能,分析其行为是否符合预期,并且能否有效应对各种情况。
  7. 迭代优化:根据评估结果,对模型和训练策略进行调整和优化,不断改进智能体的性能和稳定性。

以上是一个基本的设计和实现方案,实际项目中可能还涉及到更多的细节和挑战,如安全性保障、仿真环境与真实环境的一致性等问题。因此,在实施过程中需要综合考虑各种因素,以确保系统的稳定性和安全性。

LLAMA2部分

  • LLAMA2结构https://blog.csdn.net/sikh_0529/article/details/134375318
  • 旋转位置嵌入https://www.zhihu.com/tardis/zm/art/647109286?source_id=1005

bert模型细节 https://www.zhihu.com/question/534763354

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

Qlora https://zhuanlan.zhihu.com/p/618894919

RLHF https://zhuanlan.zhihu.com/p/631238431

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

fine-tuningllama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

fine-tuning Llama 2 with PEFT's QLoRahttps://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

常见考察题

神经网络权重初始化 https://blog.csdn.net/kebu12345678/article/details/103084851

https://zhuanlan.zhihu.com/p/667048896

https://zhuanlan.zhihu.com/p/643560888

https://zhuanlan.zhihu.com/p/599016986)

softmax如何防止指数上溢

在计算softmax函数时,指数上溢是一个常见的问题,特别是当输入的数值非常大时,指数函数的计算结果可能会溢出。为了解决这个问题,可以采取以下几种方法:

  1. 数值稳定性技巧:为了避免指数函数的溢出,可以将输入的数值减去一个常数,使得输入相对较小,从而减少指数函数的值。通常,可以通过找到输入向量中的最大值,并将所有元素减去这个最大值来实现数值稳定性。

    image-20240222173542613

    这样做可以保持相对稳定,防止指数函数的溢出。

  2. 利用性质:softmax函数的分子和分母同时除以一个相同的常数并不会改变函数的值。因此,我们可以在计算softmax时,将所有输入向量的值都减去向量中的最大值,然后进行softmax计算。

以上两种方法都可以有效地避免指数上溢的问题,并保持softmax函数的数值稳定性。在实际应用中,通常会使用这些技巧来计算softmax函数,以确保模型的稳定性和数值精度。

]]>
- - - - - 深度学习 - - - - - - - 人工智能 - - 深度学习 - - - -
- - - 【后端开发】Go-kit与Gin框架 diff --git a/page/2/index.html b/page/2/index.html index 1032d0f..9f0a906 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -202,67 +202,6 @@ - -
@@ -826,11 +765,74 @@

+ + diff --git a/page/3/index.html b/page/3/index.html deleted file mode 100644 index abdef70..0000000 --- a/page/3/index.html +++ /dev/null @@ -1,390 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LIHAIBIN'S BLOG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- - - - - - -
- -
- -
- - - - - - - - - - - - - - - - -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/search.xml b/search.xml index fb121df..1c1c7bf 100644 --- a/search.xml +++ b/search.xml @@ -4367,231 +4367,6 @@ system,能够产生极高的精度;直接测量速度和距离,这进一 智能安全 - - 【深度学习】DeepL知识汇总 - /2024/01/30/dl_summary/ - 深度学习知识汇总 -

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

- -

训练与模型

- -

逻辑回归和线性回归

-

线性回归解决的是回归问题,逻辑回归相当于是线性回归的基础上,来解决分类问题

-线性回归(Linear Regression) \[ -\begin{aligned} -&f_{w, b}(x)=\sum_i w_i x_i+b\\ -\end{aligned} -\] 逻辑回归(Logistic Regression) $$ -\[\begin{aligned} - -&f_{w, b}(x)=\sigma\left(\sum_i w_i x_i+b\right) -\end{aligned}\] -

$$ 逻辑回归可以理解为在线性回归后加了一个 sigmoid -函数。将线性回归变成一个0~1输出的分类问题。逻辑回归本质上是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归的。可以说,逻辑回归都是以线性回归为理论支持的,只不过逻辑回归可以轻松解决 -0/1 分类问题。

-

深度学习模型的参数范围

-

因为参数越小代表模型越简单,越是复杂的模型,越是尝试对所有样本进行拟合,包括异常点。这就会造成在较小的区间中产生较大的波动,这个较大的波动也会反映在这个区间的导数比较大。只有越大的参数才可能产生较大的导数。因此参数越小,模型就越简单。

-

实现参数稀疏

-

参数的稀疏,在一定程度上实现了特征的选择。一般而言,大部分特征对模型是没有贡献的。这些没有用的特征虽然可以减少训练集上的误差,但是对测试集的样本,反而会产生干扰。稀疏参数的引入,可以将那些无用的特征的权重置为0

-

Batch -size的大小对学习率的影响

-
    -
  • batch-size大,学习率也可以取得大一点,而且,batch-size大通常更新次数少,所以需要更多的epoch才能让loss收敛。
  • -
  • batch-size小,学习率应该取得小一点,取的大会发生nan(梯度爆炸了),batch-size小通常更新次数多,较少的epoch就课可以让loss收敛,但是缺点是训练过程慢。
  • -
-

为什么batch-size小,学习率取的大会发生nan? -学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)

-

优化器于损失函数

-

优化器optimizer和损失函数loss function的区别:

-
    -
  1. 优化器定义了哪些参数是要用来更新的,并且设置了更新的方式(学习率、动量、SGD等),还有一些权重衰减的设置。
  2. -
  3. 损失函数是用来计算损失的,也可以说损失函数是负责反向传播求导用的
  4. -
-

训练中loss快速增大

-

训练过程中发现loss快速增大应该从哪些方面考虑?

-
    -
  1. 学习率过大,导致学习的过程非常不平稳导致的损失值快速增大
  2. -
  3. 训练的样本中存在坏数据,造成了数据的污染
  4. -
-

梯度消失和梯度爆炸

-

梯度消失的原因和解决

-
    -
  1. 隐藏层的层数过多

    -

    反向传播求梯度时的链式求导法则,某部分梯度小于1,则多层连乘后出现梯度消失

  2. -
  3. 采用了不合适的激活函数

    -

    如sigmoid函数的最大梯度为1/4,这意味着隐藏层每一层的梯度均小于1(权值小于1时),出现梯度消失

  4. -
  5. 解决方法:1、relu激活函数,使导数衡为1 2、batch norm -3、残差结构

  6. -
-

梯度爆炸的原因和解决办法

-
    -
  1. 隐藏层的层数过多,某部分梯度大于1,则多层连乘后,梯度呈指数增长,产生梯度爆炸。
  2. -
  3. 权重初始值太大,求导时会乘上权重
  4. -
  5. 解决方法:1、梯度裁剪 2、权重L1/L2正则化 3、残差结构 4、batch -norm
  6. -
-

如何取消张量梯度

-

可以选择使用torch.no_grad来将某个张量的值取消计算梯度,model.eval -vs和torch.no_grad区别

-
    -
  • model.eval(): -依然计算梯度,但是不反传;dropout层保留概率为1;batchnorm层使用全局的mean和var
  • -
  • with torch.no_grad: 不计算梯度
  • -
-

Dropout和BatchNorm

-

Dropout和Batch norm能否一起使用?

-

可以,但是只能将Dropout放在Batch -norm之后使用。因为Dropout训练时会改变输入X的方差,从而影响Batch -norm训练过程中统计的滑动方差值;而测试时没有Dropout,输入X的方差和训练时不一致,这就导致Batch -norm测试时期望的方差和训练时统计的有偏差。

-

PyTorch实现基础网络

-
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

-

-

PyTorch几类优化器

- -

Adam与SGD的区别

-

SGD缺点是其更新方向完全依赖于当前batch计算出的梯度,因而十分不稳定。

-

Adam的优点主要在于:

-
    -
  • 考虑历史步中的梯度更新信息,能够降低梯度更新噪声。
  • -
  • 此外经过偏差校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
  • -
-

但是Adam也有其自身问题:可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。二者似乎都没法很好避免局部最优问题。

-

Norm归一化

- -

Batch Norm代码实现

-
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta
-

Transformer部分

- -

残差结构的设计

-

残差结构设计思想:残差网络的本质也是解决梯度消失/爆炸的问题,只不过是在网络结构层面的改变 -残差网络的出现解决了构建深层神经网络时网络退化即梯度消失/爆炸的问题。残差结构主要设计有两个,快捷连接(shortcut -connection)和恒等映射(identity -mapping),快捷连接使得残差变得可能,而恒等映射使得网络变深,恒等映射主要有两个:跳跃连接和激活函数

-

PyTorch实现Attention

-

自注意力机制的实现

-
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

-

多头注意力机制的实现

-
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)
-

PyTorch实现Transformer

-
import torch
import torch.nn as nn
import torch.nn.functional as F

class MultiHeadAttention(nn.Module):
def __init__(self, embed_dim, num_heads):
super(MultiHeadAttention, self).__init__()
self.num_heads = num_heads
self.head_dim = embed_dim // num_heads

self.query_fc = nn.Linear(embed_dim, embed_dim)
self.key_fc = nn.Linear(embed_dim, embed_dim)
self.value_fc = nn.Linear(embed_dim, embed_dim)
self.fc_out = nn.Linear(embed_dim, embed_dim)

def forward(self, query, key, value, mask=None):
batch_size = query.shape[0]

# Linearly project queries, keys, and values
Q = self.query_fc(query)
K = self.key_fc(key)
V = self.value_fc(value)

# Split the embedding into num_heads
Q = Q.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
K = K.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)
V = V.view(batch_size, -1, self.num_heads, self.head_dim).permute(0, 2, 1, 3)

# Calculate the attention scores
scores = torch.matmul(Q, K.permute(0, 1, 3, 2)) / torch.sqrt(torch.tensor(self.head_dim, dtype=torch.float32))

if mask is not None:
scores = scores.masked_fill(mask == 0, float("-1e20"))

# Apply softmax to get attention probabilities
attention_weights = F.softmax(scores, dim=-1)

# Apply dropout
attention_weights = F.dropout(attention_weights, p=0.1, training=self.training)

# Multiply the attention weights with the values
output = torch.matmul(attention_weights, V)

# Concatenate multi-heads and project
output = output.permute(0, 2, 1, 3).contiguous().view(batch_size, -1, embed_dim)
output = self.fc_out(output)

return output, attention_weights

class PositionwiseFeedforward(nn.Module):
def __init__(self, embed_dim, hidden_dim):
super(PositionwiseFeedforward, self).__init__()
self.fc1 = nn.Linear(embed_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, embed_dim)

def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x

class EncoderLayer(nn.Module):
def __init__(self, embed_dim, num_heads, hidden_dim):
super(EncoderLayer, self).__init__()
self.multihead_attention = MultiHeadAttention(embed_dim, num_heads)
self.feed_forward = PositionwiseFeedforward(embed_dim, hidden_dim)
self.layer_norm1 = nn.LayerNorm(embed_dim)
self.layer_norm2 = nn.LayerNorm(embed_dim)

def forward(self, x, mask=None):
# Multi-Head Attention
residual = x
x, _ = self.multihead_attention(x, x, x, mask)
x = self.layer_norm1(x + residual)

# Feed Forward
residual = x
x = self.feed_forward(x)
x = self.layer_norm2(x + residual)

return x

class TransformerEncoder(nn.Module):
def __init__(self, vocab_size, embed_dim, num_layers, num_heads, hidden_dim):
super(TransformerEncoder, self).__init__()
self.embedding = nn.Embedding(vocab_size, embed_dim)
self.layers = nn.ModuleList([EncoderLayer(embed_dim, num_heads, hidden_dim) for _ in range(num_layers)])

def forward(self, x, mask=None):
x = self.embedding(x)
for layer in self.layers:
x = layer(x, mask)
return x

-

强化学习部分

-

PyTorch实现DQN

-
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

-

PyTorch实现PPO

-
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

-

自动驾驶和RL联系与设计

-

设计和实现一个基于强化学习的自动驾驶决策规划系统是一个复杂而且需要深入思考的任务。下面是一个基本的设计和实现方案:

-
设计方案
-
    -
  1. 环境建模: -首先,需要对自动驾驶的环境进行建模,包括车辆、道路、交通规则、障碍物、目标等。环境可以使用基于物理的仿真环境,例如CARLA,或者基于虚拟仿真的环境。
  2. -
  3. 状态空间定义: -定义自动驾驶车辆的状态空间,这可能包括车辆的位置、速度、方向、周围车辆的位置和速度等信息。
  4. -
  5. 动作空间定义: -定义自动驾驶车辆可以执行的动作空间,如加速、减速、转向等。
  6. -
  7. 奖励函数设计: -设计奖励函数来评估每个状态下执行的动作,以指导智能体的学习。奖励函数应该考虑安全性、效率、舒适性等因素。
  8. -
  9. 智能体模型选择: -选择合适的强化学习算法和模型架构来训练智能体,例如深度Q网络(DQN)、深度确定性策略梯度(DDPG)、双重深度确定性策略梯度(TD3)等。
  10. -
  11. 训练策略: -定义训练策略,包括学习率、优化器、探索策略等。
  12. -
  13. 评估与测试: -在仿真环境中对训练好的智能体进行评估和测试,检查其性能和鲁棒性。
  14. -
-
实现方案
-
    -
  1. 环境建模: 使用CARLA等仿真平台进行环境建模。
  2. -
  3. 状态空间和动作空间定义: -编写代码从环境中获取车辆状态信息,并定义可以执行的动作。
  4. -
  5. 奖励函数设计: -根据项目需求和目标设计奖励函数。
  6. -
  7. 智能体模型选择: -根据任务选择合适的强化学习算法和模型架构,例如使用深度神经网络作为Q值函数的估计器。
  8. -
  9. 训练智能体: -使用收集的数据对智能体进行训练,调整网络参数,以最大化奖励函数。
  10. -
  11. 评估与测试: -在仿真环境中评估训练后的智能体的性能,分析其行为是否符合预期,并且能否有效应对各种情况。
  12. -
  13. 迭代优化: -根据评估结果,对模型和训练策略进行调整和优化,不断改进智能体的性能和稳定性。
  14. -
-

以上是一个基本的设计和实现方案,实际项目中可能还涉及到更多的细节和挑战,如安全性保障、仿真环境与真实环境的一致性等问题。因此,在实施过程中需要综合考虑各种因素,以确保系统的稳定性和安全性。

-

LLAMA2部分

- -

bert模型细节 https://www.zhihu.com/question/534763354

-

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

-

Qlora https://zhuanlan.zhihu.com/p/618894919

-

RLHF https://zhuanlan.zhihu.com/p/631238431

-

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

-

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

-

fine-tuning -llama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

-

fine-tuning Llama 2 with PEFT's QLoRahttps://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

-

常见考察题

-

神经网络权重初始化 https://blog.csdn.net/kebu12345678/article/details/103084851

-

https://zhuanlan.zhihu.com/p/667048896

-

https://zhuanlan.zhihu.com/p/643560888

-

https://zhuanlan.zhihu.com/p/599016986)

-

softmax如何防止指数上溢

-

在计算softmax函数时,指数上溢是一个常见的问题,特别是当输入的数值非常大时,指数函数的计算结果可能会溢出。为了解决这个问题,可以采取以下几种方法:

-
    -
  1. 数值稳定性技巧:为了避免指数函数的溢出,可以将输入的数值减去一个常数,使得输入相对较小,从而减少指数函数的值。通常,可以通过找到输入向量中的最大值,并将所有元素减去这个最大值来实现数值稳定性。

    -

    image-20240222173542613

    -

    这样做可以保持相对稳定,防止指数函数的溢出。

  2. -
  3. 利用性质:softmax函数的分子和分母同时除以一个相同的常数并不会改变函数的值。因此,我们可以在计算softmax时,将所有输入向量的值都减去向量中的最大值,然后进行softmax计算。

  4. -
-

以上两种方法都可以有效地避免指数上溢的问题,并保持softmax函数的数值稳定性。在实际应用中,通常会使用这些技巧来计算softmax函数,以确保模型的稳定性和数值精度。

-]]>
- - 深度学习 - - - 人工智能 - 深度学习 - -
【多智能体强化学习】基于自动分组的价值函数分解 /2023/11/22/marl_autogroup/ @@ -7751,7 +7526,93 @@ href="https://so.csdn.net/so/search?q=conda&spm=1001.2101.3001.7020">conda【深度学习】DeepL|LLM基础知识 /2024/03/05/dl_llm_basic/ -

深度学习&LLM基础

+

这里将分享深度学习和大语言模型的基础知识和内容

+

深度学习基础

+

训练细节

+

1.逻辑回归和线性回归

+

线性回归解决的是回归问题,逻辑回归相当于是线性回归的基础上,来解决分类问题

+线性回归(Linear Regression) \[ +\begin{aligned} +&f_{w, b}(x)=\sum_i w_i x_i+b\\ +\end{aligned} +\] 逻辑回归(Logistic Regression) $$ +\[\begin{aligned} + +&f_{w, b}(x)=\sigma\left(\sum_i w_i x_i+b\right) +\end{aligned}\] +

$$ 逻辑回归可以理解为在线性回归后加了一个 sigmoid +函数。将线性回归变成一个0~1输出的分类问题。逻辑回归本质上是一个线性回归模型,因为除去sigmoid映射函数关系,其他的步骤,算法都是线性回归的。可以说,逻辑回归都是以线性回归为理论支持的,只不过逻辑回归可以轻松解决 +0/1 分类问题。

+

2. 深度学习模型的参数范围

+

因为参数越小代表模型越简单,越是复杂的模型,越是尝试对所有样本进行拟合,包括异常点。这就会造成在较小的区间中产生较大的波动,这个较大的波动也会反映在这个区间的导数比较大。只有越大的参数才可能产生较大的导数。因此参数越小,模型就越简单。

+

3. 实现参数稀疏

+

参数的稀疏,在一定程度上实现了特征的选择。一般而言,大部分特征对模型是没有贡献的。这些没有用的特征虽然可以减少训练集上的误差,但是对测试集的样本,反而会产生干扰。稀疏参数的引入,可以将那些无用的特征的权重置为0

+

4. Batch +size的大小对学习率的影响

+
    +
  • batch-size大,学习率也可以取得大一点,而且,batch-size大通常更新次数少,所以需要更多的epoch才能让loss收敛。
  • +
  • batch-size小,学习率应该取得小一点,取的大会发生nan(梯度爆炸了),batch-size小通常更新次数多,较少的epoch就课可以让loss收敛,但是缺点是训练过程慢。
  • +
+

为什么batch-size小,学习率取的大会发生nan? +学习率较高的情况下,直接影响到每次更新值的程度比较大,走的步伐因此也会大起来。过大的学习率会导致无法顺利地到达最低点,稍有不慎就会跳出可控制区域,此时我们将要面对的就是损失成倍增大(跨量级)

+

5. 优化器于损失函数

+

优化器optimizer和损失函数loss function的区别:

+
    +
  1. 优化器定义了哪些参数是要用来更新的,并且设置了更新的方式(学习率、动量、SGD等),还有一些权重衰减的设置。
  2. +
  3. 损失函数是用来计算损失的,也可以说损失函数是负责反向传播求导用的
  4. +
+

6. 训练中loss快速增大

+

训练过程中发现loss快速增大应该从哪些方面考虑?

+
    +
  1. 学习率过大,导致学习的过程非常不平稳导致的损失值快速增大
  2. +
  3. 训练的样本中存在坏数据,造成了数据的污染
  4. +
+

7. 梯度消失和梯度爆炸

+

梯度消失的原因和解决

+
    +
  1. 隐藏层的层数过多

    +

    反向传播求梯度时的链式求导法则,某部分梯度小于1,则多层连乘后出现梯度消失

  2. +
  3. 采用了不合适的激活函数

    +

    如sigmoid函数的最大梯度为1/4,这意味着隐藏层每一层的梯度均小于1(权值小于1时),出现梯度消失

  4. +
  5. 解决方法:1、relu激活函数,使导数衡为1 2、batch norm +3、残差结构

  6. +
+

梯度爆炸的原因和解决办法

+
    +
  1. 隐藏层的层数过多,某部分梯度大于1,则多层连乘后,梯度呈指数增长,产生梯度爆炸。
  2. +
  3. 权重初始值太大,求导时会乘上权重
  4. +
  5. 解决方法:1、梯度裁剪 2、权重L1/L2正则化 3、残差结构 4、batch +norm
  6. +
+

8. 如何取消张量梯度

+

可以选择使用torch.no_grad来将某个张量的值取消计算梯度,model.eval +vs和torch.no_grad区别

+
    +
  • model.eval(): +依然计算梯度,但是不反传;dropout层保留概率为1;batchnorm层使用全局的mean和var
  • +
  • with torch.no_grad: 不计算梯度
  • +
+

9.Dropout和BatchNorm

+

Dropout和Batch norm能否一起使用?

+

可以,但是只能将Dropout放在Batch +norm之后使用。因为Dropout训练时会改变输入X的方差,从而影响Batch +norm训练过程中统计的滑动方差值;而测试时没有Dropout,输入X的方差和训练时不一致,这就导致Batch +norm测试时期望的方差和训练时统计的有偏差。

+

其余参考

+

神经网络权重初始化 https://blog.csdn.net/kebu12345678/article/details/103084851

+

https://zhuanlan.zhihu.com/p/667048896

+

https://zhuanlan.zhihu.com/p/643560888

+

https://zhuanlan.zhihu.com/p/599016986

+ +

LLM基础

1.Attention

1.1 讲讲对Attention的理解

@@ -8260,7 +8121,74 @@ Norm结构往往更容易训练,但最终效果通常不如Post Norm
Norm更容易训练好理解,因为它的恒等路径更突出,但为什么它效果反而没那么好呢?这个是解释的链接:https://kexue.fm/archives/9009

4 PyTorch几类优化器

-

4.1 Batch Norm

+

4.1 梯度下降 (Gradient Descent, +GD):

+ +

RMSprop (Root Mean Square +Propagation):

+ +

Adam (Adaptive Moment +Estimation):

+ +

这些优化器之间的区别主要体现在对梯度的处理方式上, +以及对学习率的自适应调整。例如, +动量优化器可以加速收玫并帮助逃离局部最优点, +而自适应学习率优化器则可以根据梯度情况动态调整学习率, +适应不同参数的更新速度。

5.位置编码

5.1 绝对位置编码

不同于RNN、CNN等模型,对于Transformer模型来说,位置编码的加入是必不可少的,因为纯粹的Attention模块是无法捕捉输入顺序的,即无法区分不同位置的Token。为此我们大体有两个选择:

@@ -9001,6 +8929,47 @@ src="https://gitee.com/lihaibineric/picgo/raw/master/pic/image-20240314113741425 alt="进制转换的示意图" /> +

其他文章参考

+

深度学习八股文,这里将会收集深度学习中的基本概念和常见的问题,以下是主要的参考文章

+ +

LLAMA2结构https://blog.csdn.net/sikh_0529/article/details/134375318

+

旋转位置嵌入https://www.zhihu.com/tardis/zm/art/647109286?source_id=1005

+

bert模型细节 https://www.zhihu.com/question/534763354

+

为什么Bert三个embedding可以相加 https://www.zhihu.com/question/374835153/answer/1080315948

+

Qlora https://zhuanlan.zhihu.com/p/618894919

+

RLHF https://zhuanlan.zhihu.com/p/631238431

+

LLAMA2 colabhttps://zhuanlan.zhihu.com/p/652588148

+

LLAMA2+QLora微调大模型https://www.bilibili.com/video/BV1594y1y76m/?spm_id_from=333.337.search-card.all.click&vd_source=9710fe8f4dbfeb6bd0b7202815b341c2

+

fine-tuning +llama2https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/

+

fine-tuning Llama 2 with PEFT's +QLoRa[https://ukey.co/blog/finetune-llama-2-peft-qlora-huggingface/](

+

基础代码

+

PyTorch实现基础网络

+
import torch
import torch.nn as nn
import torch.optim as optim

class BasicNet(nn.Module):
def __init__(self, input_dim=784, hidden_dim1=256, hidden_dim2=128, output_dim=10):
super(BasicNet, self).__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.fc3 = nn.Linear(hidden_dim2, output_dim)
self.relu = nn.ReLU()
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)

def forward(self, x):
x = self.relu(self.batchnorm1(self.fc1(x)))
x = self.relu(self.batchnorm2(self.fc2(x)))
x = self.fc3(x)
return x

# 创建模型
model = BasicNet()

# 定义损失函数
criterion = nn.CrossEntropyLoss()

# 定义优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train(model, train_loader, criterion, optimizer, num_epochs=5):
model.train()
for epoch in range(num_epochs):
running_loss = 0.0
for i, (inputs, labels) in enumerate(train_loader):
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if (i+1) % 100 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(train_loader)}], Loss: {running_loss/100:.4f}')
running_loss = 0.0

# 保存模型
def save_model(model, filepath):
torch.save(model.state_dict(), filepath)

# 加载模型
def load_model(model, filepath):
model.load_state_dict(torch.load(filepath))
model.eval() # 设置为评估模式

# 示例数据加载
# train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)

# 示例训练过程
# train(model, train_loader, criterion, optimizer)

# 示例模型保存
# save_model(model, 'model.pth')

# 示例模型加载
# loaded_model = BasicNet()
# load_model(loaded_model, 'model.pth')

+

Batch Norm代码实现

+
class MyBN:
def __init__(self, momentum=0.01, eps=1e-5, feat_dim=2):
self._running_mean = np.zeros(shape = (feat_dim,))
self._running_var = np.ones(shape = (fear_dim,))
self._momentum = momentum
#防止分母计算为0
self._eps = eps

#对应batch norm中需要更新beta 和 gamma, 采用pytorch文档中的初始化
self._beta = np.zeros(shape=(feat_dim,))
self._gamma = np.ones(shape=(feat_dim,))


def batch_norm(self, x):
if self.training:
x_mean = x.mean(axis=0)
x_var = x.var(axis=0)
#对应running_mean的更新公式
self._running_mean = (1-self._momentum)*x_mean +self._momentum*self._running_mean
self._running_var = (1-self._momentum)*x_var + self._momentum*self._running_var
#对应论文中计算BN公式
x_hat = (x-x_mean)/np.sqrt(x_var+self._eps)
else:
x_hat = (x-self._running_mean)/np.sqrt(self._running_var+self._eps)
return self._gamma*x_hat + self._beta
+

PyTorch实现Attention

+

自注意力机制的实现

+
from math import sqrt
import torch
import torch.nn as nn

class SelfAttention(nn.Module):
def __init__(self, dim_in, dim_k, dim_v):
super(SelfAttention, self).__init__()
self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.linear_q = nn.Linear(dim_in, dim_k, bias=False)
self.linear_k = nn.Linear(dim_in, dim_k, bias=False)
self.linear_v = nn.Linear(dim_in, dim_v, bias=False)
self._norm_fact = 1/sqrt(dim_k)


def forward(self, x):
batch, n, dim_in = x.shape
assert dim_in == self.dim_in

q = self.linear_q(x) #batch, n, dim_k
k = self.linear_k(x)
v = self.linear_v(x)

dist = torch.bmm(q, k.transpose(1,2))* self._norm_fact #batch, n, n
dist = torch.softmax(dist, dim=-1)

att = torch.bmm(dist, v)
return att

+

多头注意力机制的实现

+
from math import sqrt
import torch
import torch.nn as nn

class MultiHeadAttention(nn.Module):
#dim_in input dimention
#dim_k kq dimention
#dim_v value dimention
#num_heads number of heads

def __init__(self, dim_in, dim_k, dim_v, num_heads=8):
super(MultiHeadAttention, self).__init__()
assert dim_k% num_heads ==0 and dim_v% num_heads ==0

self.dim_in = dim_in
self.dim_k = dim_k
self.dim_v = dim_v
self.num_heads = num_heads
self.linear_q = nn.Linear(dim_in, dim_k, bias==False)
self.linear_k = nn.Linear(dim_in, dim_k, bias==False)
self.linear_v = nn.Linear(dim_in, dim_v, bias==False)
self._norm_fact = 1/sqrt(dim_k//num_heads)

def forwards(self, x):
# x: tensor of shape(batch, n, dim_in)
batch, n, dim_in = x.shape
assert dim_in = self.dim_in

nh = self.num_heads
dk = self.dim_k // nh
dv = self.dim_v // nh

q = self.linear_q(x).reshape(batch, n, nh, dk).transpose(1, 2)
k = self.linear_k(x).reshape(batch, n, nh, dk).transpose(1, 2)
v = self.linear_v(x).reshape(batch, n, nk, dk).transpose(1, 2)

dist = torch.matmul(q, k.transpose(2,3))*self._norm_fact
dist = torch.softmax(dist, dim=-1)

att = torch.matmul(dist, v)
att = att.transpose(1,2).reshape(batch, n, self.dim_v)
+

PyTorch实现DQN

+
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class DQN(nn.Module):
def __init__(self, input_dim, output_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, output_dim)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

class DQNAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = DQN(input_dim, output_dim).to(self.device)
self.target_net = DQN(input_dim, output_dim).to(self.device)
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval()
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state, epsilon):
if np.random.rand() < epsilon:
return np.random.randint(self.output_dim)
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
q_values = self.policy_net(state)
return q_values.max(1)[1].item()

def train(self, replay_buffer, batch_size):
if len(replay_buffer) < batch_size:
return
transitions = replay_buffer.sample(batch_size)
batch = Transition(*zip(*transitions))
state_batch = torch.FloatTensor(batch.state).to(self.device)
next_state_batch = torch.FloatTensor(batch.next_state).to(self.device)
action_batch = torch.LongTensor(batch.action).unsqueeze(1).to(self.device)
reward_batch = torch.FloatTensor(batch.reward).unsqueeze(1).to(self.device)
done_batch = torch.FloatTensor(batch.done).unsqueeze(1).to(self.device)

current_q_values = self.policy_net(state_batch).gather(1, action_batch)
next_q_values = self.target_net(next_state_batch).max(1)[0].unsqueeze(1)
target_q_values = reward_batch + (1 - done_batch) * self.gamma * next_q_values

loss = self.loss_fn(current_q_values, target_q_values.detach())
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_net(self):
self.target_net.load_state_dict(self.policy_net.state_dict())

class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
self.position = 0

def push(self, state, action, reward, next_state, done):
if len(self.buffer) < self.capacity:
self.buffer.append(None)
self.buffer[self.position] = (state, action, reward, next_state, done)
self.position = (self.position + 1) % self.capacity

def sample(self, batch_size):
return random.sample(self.buffer, batch_size)

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

Transition = namedtuple('Transition', ('state', 'action', 'reward', 'next_state', 'done'))

+

PyTorch实现PPO

+
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class ActorCritic(nn.Module):
def __init__(self, input_dim, output_dim):
super(ActorCritic, self).__init__()
self.fc1 = nn.Linear(input_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc_actor = nn.Linear(128, output_dim)
self.fc_critic = nn.Linear(128, 1)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
logits = self.fc_actor(x)
value = self.fc_critic(x)
return logits, value

class PPOAgent:
def __init__(self, input_dim, output_dim, gamma=0.99, lr=0.001):
self.input_dim = input_dim
self.output_dim = output_dim
self.gamma = gamma
self.lr = lr
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.policy_net = ActorCritic(input_dim, output_dim).to(self.device)
self.optimizer = optim.Adam(self.policy_net.parameters(), lr=lr)
self.loss_fn = nn.MSELoss()

def select_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
with torch.no_grad():
logits, _ = self.policy_net(state)
action_probs = torch.softmax(logits, dim=-1)
action = np.random.choice(np.arange(self.output_dim), p=action_probs.cpu().numpy().ravel())
return action

def train(self, states, actions, rewards, next_states, dones, old_log_probs, epsilon_clip=0.2, num_epochs=10):
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).unsqueeze(-1).to(self.device)
rewards = torch.FloatTensor(rewards).unsqueeze(-1).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).unsqueeze(-1).to(self.device)
old_log_probs = torch.FloatTensor(old_log_probs).unsqueeze(-1).to(self.device)

for _ in range(num_epochs):
logits, values = self.policy_net(states)
new_log_probs = torch.log_softmax(logits, dim=-1).gather(1, actions)
ratio = (new_log_probs - old_log_probs).exp()
advantages = rewards - values.detach()

surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1.0 - epsilon_clip, 1.0 + epsilon_clip) * advantages
actor_loss = -torch.min(surr1, surr2).mean()

critic_loss = 0.5 * (rewards - values).pow(2).mean()

loss = actor_loss + 0.5 * critic_loss

self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

]]> 深度学习 @@ -9552,7 +9521,7 @@ Model:将文本看作是一个整体,而不是将其拆分成句子 - 【大语言模型】有监督微调 + 【大语言模型】大模型监督微调 /2024/03/24/dl-llm-ft/

大语言模型微调

diff --git a/tags/index.html b/tags/index.html index 8d3b766..d9dd2e4 100644 --- a/tags/index.html +++ b/tags/index.html @@ -202,7 +202,7 @@ diff --git "a/tags/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" "b/tags/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" index 9f92d53..6630669 100644 --- "a/tags/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" +++ "b/tags/\344\272\272\345\267\245\346\231\272\350\203\275/index.html" @@ -202,7 +202,7 @@
-

8 posts in total

+

7 posts in total


@@ -216,12 +216,6 @@ - - -
【深度学习】DeepL知识汇总
-
- -
【多智能体强化学习】Pymarl代码分析
diff --git "a/tags/\345\244\247\350\257\255\350\250\200\346\250\241\345\236\213/index.html" "b/tags/\345\244\247\350\257\255\350\250\200\346\250\241\345\236\213/index.html" index 144b637..e80c196 100644 --- "a/tags/\345\244\247\350\257\255\350\250\200\346\250\241\345\236\213/index.html" +++ "b/tags/\345\244\247\350\257\255\350\250\200\346\250\241\345\236\213/index.html" @@ -212,7 +212,7 @@
-
【大语言模型】有监督微调
+
【大语言模型】大模型监督微调
diff --git "a/tags/\346\267\261\345\272\246\345\255\246\344\271\240/index.html" "b/tags/\346\267\261\345\272\246\345\255\246\344\271\240/index.html" index 41f4f1e..ee0a38f 100644 --- "a/tags/\346\267\261\345\272\246\345\255\246\344\271\240/index.html" +++ "b/tags/\346\267\261\345\272\246\345\255\246\344\271\240/index.html" @@ -202,7 +202,7 @@ - - diff --git "a/tags/\346\267\261\345\272\246\345\255\246\344\271\240/page/2/index.html" "b/tags/\346\267\261\345\272\246\345\255\246\344\271\240/page/2/index.html" deleted file mode 100644 index 0ec8797..0000000 --- "a/tags/\346\267\261\345\272\246\345\255\246\344\271\240/page/2/index.html" +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tags - 深度学习 - LIHAIBIN'S BLOG - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- - - - - - -
- -
- -
- -
-
- -
-
-
- - -
-

11 posts in total

-
- - - - -

2023

- - - -
【多智能体强化学习】Pymarl环境配置
-
- -
- - - - - - - -
-
-
-
-
- - - - - - - - - - - - - - -
- -
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -