AE

image.png

AE的Pytorch代码实现(MINIST数据集)

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
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader


# 定义自编码器类
class Autoencoder(nn.Module):
def __init__(self):
super(Autoencoder, self).__init__()
self.encoder = nn.Sequential(
nn.Linear(28 * 28, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 32),
nn.ReLU()
)
self.decoder = nn.Sequential(
nn.Linear(32, 64),
nn.ReLU(),
nn.Linear(64, 128),
nn.ReLU(),
nn.Linear(128, 28 * 28),
nn.Sigmoid() # 将输出限制在 [0, 1] 范围内
)

def forward(self, x):
x = self.encoder(x)
x = self.decoder(x)
return x


# 加载 MNIST 数据集
transform = transforms.Compose([transforms.ToTensor()])
train_set = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=64, shuffle=True)

# 实例化自编码器
autoencoder = Autoencoder()

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.Adam(autoencoder.parameters(), lr=0.001)

# 训练自编码器
num_epochs = 10
for epoch in range(num_epochs):
running_loss = 0.0
for data in train_loader:
inputs, _ = data
inputs = inputs.view(inputs.size(0), -1)

optimizer.zero_grad()

outputs = autoencoder(inputs)
loss = criterion(outputs, inputs)

loss.backward()
optimizer.step()

running_loss += loss.item()
print('Epoch [{}/{}], Loss: {:.4f}'.format(epoch + 1, num_epochs, running_loss / len(train_loader)))

# 保存模型
torch.save(autoencoder.state_dict(), 'autoencoder.pth')

VAE

image.png

VAE的Pytorch代码实现(MINIST数据集)

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
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn.functional as F

# 定义变分自编码器类
class VAE(nn.Module):
def __init__(self):
super(VAE, self).__init__()

self.fc1 = nn.Linear(784, 400)
self.fc21 = nn.Linear(400, 20) # 均值
self.fc22 = nn.Linear(400, 20) # 方差
self.fc3 = nn.Linear(20, 400)
self.fc4 = nn.Linear(400, 784)

def encode(self, x):
h1 = F.relu(self.fc1(x))
return self.fc21(h1), self.fc22(h1)

# 重参数化
def reparameterize(self, mu, logvar):
std = torch.exp(0.5*logvar)
eps = torch.randn_like(std)
return mu + eps*std

def decode(self, z):
h3 = F.relu(self.fc3(z))
return torch.sigmoid(self.fc4(h3))

def forward(self, x):
mu, logvar = self.encode(x.view(-1, 784))
z = self.reparameterize(mu, logvar)
return self.decode(z), mu, logvar

# 加载 MNIST 数据集
transform = transforms.Compose([transforms.ToTensor()])
train_set = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=64, shuffle=True)

# 实例化变分自编码器
vae = VAE()

# 定义损失函数和优化器
def loss_function(recon_x, x, mu, logvar):
BCE = F.binary_cross_entropy(recon_x, x.view(-1, 784), reduction='sum')
KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
return BCE + KLD

optimizer = optim.Adam(vae.parameters(), lr=0.001)

# 训练变分自编码器
def train(epoch):
vae.train()
train_loss = 0
for batch_idx, (data, _) in enumerate(train_loader):
optimizer.zero_grad()
recon_batch, mu, logvar = vae(data)
loss = loss_function(recon_batch, data, mu, logvar)
loss.backward()
train_loss += loss.item()
optimizer.step()
if batch_idx % 100 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader),
loss.item() / len(data)))

print('====> Epoch: {} Average loss: {:.4f}'.format(
epoch, train_loss / len(train_loader.dataset)))

num_epochs = 10
for epoch in range(1, num_epochs + 1):
train(epoch)

# 保存模型
torch.save(vae.state_dict(), 'vae.pth')

1.VAE损失函数构成

image.png

损失函数通常采用交叉熵损失函数。

2.VAE的重参数化

image.png

GAN

image.png

GAN的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
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
94
95
96
97
98
99
100
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import torch.nn.functional as F

# 定义生成器类
class Generator(nn.Module):
def __init__(self, latent_dim=100, image_dim=784):
super(Generator, self).__init__()

self.fc1 = nn.Linear(latent_dim, 256)
self.fc2 = nn.Linear(256, 512)
self.fc3 = nn.Linear(512, image_dim)

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

# 定义判别器类
class Discriminator(nn.Module):
def __init__(self, image_dim=784):
super(Discriminator, self).__init__()

self.fc1 = nn.Linear(image_dim, 512)
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 1)

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

# 定义生成器和判别器
latent_dim = 100
image_dim = 28 * 28
generator = Generator(latent_dim, image_dim)
discriminator = Discriminator(image_dim)

# 加载 MNIST 数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_set = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_set, batch_size=64, shuffle=True)

# 定义损失函数和优化器
criterion = nn.BCELoss() # 二元交叉熵损失函数
lr = 0.0002
optimizer_G = optim.Adam(generator.parameters(), lr=lr)
optimizer_D = optim.Adam(discriminator.parameters(), lr=lr)

# 训练生成对抗网络
def train(num_epochs):
for epoch in range(num_epochs):
for i, (real_images, _) in enumerate(train_loader):
batch_size = real_images.size(0)
real_images = real_images.view(-1, image_dim)

# 训练判别器
optimizer_D.zero_grad()
real_labels = torch.ones(batch_size, 1)
fake_labels = torch.zeros(batch_size, 1)

# 真实图片的判别结果
real_output = discriminator(real_images)
d_loss_real = criterion(real_output, real_labels)
d_loss_real.backward()

# 生成假图片并计算判别结果
noise = torch.randn(batch_size, latent_dim)
fake_images = generator(noise)
fake_output = discriminator(fake_images.detach())
d_loss_fake = criterion(fake_output, fake_labels)
d_loss_fake.backward()

d_loss = d_loss_real + d_loss_fake
optimizer_D.step()

# 训练生成器
optimizer_G.zero_grad()
output = discriminator(fake_images)
g_loss = criterion(output, real_labels)
g_loss.backward()
optimizer_G.step()

if i % 100 == 0:
print('[%d/%d][%d/%d]\tLoss_D: %.4f\tLoss_G: %.4f' %
(epoch, num_epochs, i, len(train_loader),
d_loss.item(), g_loss.item()))

num_epochs = 10
train(num_epochs)

# 保存模型
torch.save(generator.state_dict(), 'generator.pth')
torch.save(discriminator.state_dict(), 'discriminator.pth')

1.GAN损失函数构成

GAN的损失函数由两部分组成:生成器(Generator)的损失函数和判别器(Discriminator)的损失函数。生成器的损失函数旨在使生成的样本尽可能地接近真实样本,而判别器的损失函数则旨在正确区分真实样本和生成样本。

生成器的损失函数通常使用生成样本被误认为真实样本的概率来衡量,目标是最大化这个概率,使生成的样本更接近真实样本。判别器的损失函数则包括将真实样本正确分类为真实样本的概率和将生成样本正确分类为生成样本的概率,目标是最小化这两个概率之和,使判别器能够准确地区分真实样本和生成样本。

一般来说,GAN的损失函数可以表示为生成器和判别器损失函数之和的形式。两种损失一正一负,他们的和越趋近于0性能越好。一般生成器的损失函数是负值,判别器的损失函数是正值,最终要最大化生成器损失最小化判别器损失。

损失函数通常采用交叉熵损失函数。

U-Net

U-Net是一种用于图像分割的深度学习架构,由Ronneberger等人于2015年提出。它的名字源自其U形状的网络结构。U-Net结合了卷积神经网络(CNN)的编码器和解码器结构,以实现精确的图像分割。其主要特点包括对称的编码器和解码器路径,跳跃连接以保留更多低级别特征信息,以及使用反卷积操作进行上采样。这使得U-Net在医学图像分割等领域取得了很好的效果,因为它能够准确地提取图像中的目标并保留细节信息。

image.png

U-Net的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
import torch
import torch.nn as nn

# 定义U-Net的编码器部分
class UNetEncoderBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super(UNetEncoderBlock, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):
x = self.conv(x)
x = self.relu(x)
return x

# 定义U-Net的解码器部分
class UNetDecoderBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super(UNetDecoderBlock, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
self.relu = nn.ReLU(inplace=True)

def forward(self, x):
x = self.conv(x)
x = self.relu(x)
return x

# 定义完整的U-Net网络结构
class UNet(nn.Module):
def __init__(self, in_channels, out_channels):
super(UNet, self).__init__()
# 编码器部分
self.encoder1 = UNetEncoderBlock(in_channels, 64)
self.encoder2 = UNetEncoderBlock(64, 128)
# 解码器部分
self.decoder1 = UNetDecoderBlock(128, 64)
self.decoder2 = UNetDecoderBlock(64, out_channels)

def forward(self, x):
# 编码器过程
x1 = self.encoder1(x)
x2 = self.encoder2(x1)
# 解码器过程
x = self.decoder1(x2)
x = self.decoder2(x)
return x

# 示例使用
# 定义输入图像通道数和输出图像通道数
in_channels = 3
out_channels = 1 # 二分类问题
# 创建U-Net模型实例
model = UNet(in_channels, out_channels)
# 定义损失函数和优化器
criterion = nn.BCEWithLogitsLoss() # 二分类交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

1.U-Net与VAE都是编码器解码器结构,它们有什么区别

VAE和 U-Net 有几个显著的区别:

  1. 任务类型
    • VAE 通常用于生成模型,旨在学习数据的潜在表示,并生成与输入数据相似的新样本。
    • U-Net 主要用于图像分割任务,其目标是从输入图像中预测像素级别的标签。
  2. 结构设计
    • VAE 通常由编码器和解码器组成,编码器将输入数据映射到潜在空间中的分布参数,解码器将潜在表示重新映射回数据空间。
    • U-Net 结构通常由对称的编码器和解码器组成,其中编码器用于提取图像的特征,解码器则用于将特征映射回输入图像的相同尺寸。
  3. 应用领域
    • VAE 适用于生成各种类型的数据,如图像、文本等,它可以用于图像生成、图像重建、图像去噪等任务。
    • U-Net 主要用于图像分割任务,如医学图像分割、卫星图像分割等领域。

虽然 VAE 和 U-Net 都是深度学习模型,但它们的设计和应用场景有很大的区别,因此不能将它们视为同一类模型。

Transformer

Transformer的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
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import torch
import torch.nn as nn
import torch.nn.functional as F

class SelfAttention(nn.Module):
def __init__(self, embed_size, heads):
super(SelfAttention, self).__init__()
self.embed_size = embed_size
self.heads = heads
self.head_dim = embed_size // heads

assert (
self.head_dim * heads == embed_size
), "Embedding size needs to be divisible by heads"

self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
self.fc_out = nn.Linear(heads * self.head_dim, embed_size)

def forward(self, values, keys, query, mask):
N = query.shape[0]
value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]

values = values.reshape(N, value_len, self.heads, self.head_dim)
keys = keys.reshape(N, key_len, self.heads, self.head_dim)
queries = query.reshape(N, query_len, self.heads, self.head_dim)

values = self.values(values)
keys = self.keys(keys)
queries = self.queries(queries)

energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
if mask is not None:
energy = energy.masked_fill(mask == 0, float("-1e20"))

attention = torch.softmax(energy / (self.embed_size ** (1/2)), dim=3)

out = torch.einsum("nhql,nlhd->nqhd", [attention, values]).reshape(
N, query_len, self.heads * self.head_dim
)

out = self.fc_out(out)
return out


class TransformerBlock(nn.Module):
def __init__(self, embed_size, heads, dropout, forward_expansion):
super(TransformerBlock, self).__init__()
self.attention = SelfAttention(embed_size, heads)
self.norm1 = nn.LayerNorm(embed_size)
self.norm2 = nn.LayerNorm(embed_size)

self.feed_forward = nn.Sequential(
nn.Linear(embed_size, forward_expansion * embed_size),
nn.ReLU(),
nn.Linear(forward_expansion * embed_size, embed_size),
)

self.dropout = nn.Dropout(dropout)

def forward(self, value, key, query, mask):
attention = self.attention(value, key, query, mask)

x = self.dropout(self.norm1(attention + query))
forward = self.feed_forward(x)
out = self.dropout(self.norm2(forward + x))
return out

class Encoder(nn.Module):
def __init__(
self,
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
):
super(Encoder, self).__init__()
self.embed_size = embed_size
self.device = device
self.word_embedding = nn.Embedding(src_vocab_size, embed_size)
self.position_embedding = nn.Embedding(max_length, embed_size)

self.layers = nn.ModuleList(
[
TransformerBlock(
embed_size,
heads,
dropout=dropout,
forward_expansion=forward_expansion,
)
for _ in range(num_layers)
]
)

self.dropout = nn.Dropout(dropout)

def forward(self, x, mask):
N, seq_length = x.shape
positions = torch.arange(0, seq_length).expand(N, seq_length).to(self.device)
out = self.dropout(
(self.word_embedding(x) + self.position_embedding(positions))
)

for layer in self.layers:
out = layer(out, out, out, mask)

return out

class Transformer(nn.Module):
def __init__(
self,
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
):
super(Transformer, self).__init__()
self.encoder = Encoder(
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
)

def forward(self, x, mask):
x = self.encoder(x, mask)
return x

# 示例使用
# 定义参数
src_vocab_size = 1000 # 假设源语言词汇表大小
embed_size = 512
num_layers = 6
heads = 8
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
forward_expansion = 4
dropout = 0.1
max_length = 100

# 创建Transformer模型实例
model = Transformer(
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length,
).to(device)

# 定义输入和mask
input_sequence = torch.tensor([[1, 5, 6, 4, 3, 9, 5, 2, 0], [5, 6, 7, 6, 2, 8, 2, 1, 0]]).to(device)
mask = torch.tensor([[1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 0]]).to(device)

# 前向传播
output = model(input_sequence, mask)
print(output.shape) # 输出形状为 (2, 9, 512)

面试问题汇总(杂)

1.过拟合怎么解决

dropout

dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。dropout为什么能防止过拟合,可以通过以下几个方面来解释:

  1. 它强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,达到好的效果。消除减弱了神经元节点间的联合适应性,增强了泛化能力。
  2. 类似于bagging的集成效果
  3. 对于每一个dropout后的网络,进行训练时,相当于做了Data Augmentation,因为,总可以找到一个样本,使得在原始的网络上也能达到dropout单元后的效果。 比如,对于某一层,dropout一些单元后,形成的结果是(1.5,0,2.5,0,1,2,0),其中0是被drop的单元,那么总能找到一个样本,使得结果也是如此。这样,每一次dropout其实都相当于增加了样本。

dropout在测试时,并不会随机丢弃神经元,而是使用全部所有的神经元,同时,所有的权重值都乘上1-p,p代表的是随机失活率。

L1和L2正则化

L1正则化和L2正则化的主要区别在于它们对权重的惩罚方式不同。L1正则化倾向于产生稀疏权重,即使很多权重趋向于零,而L2正则化倾向于使权重分散在各个特征上,但不会将它们变为零。

增加数据(数据增强)

大部分过拟合产生的原因是因为数据量太少了. 如果我们有成千上万的数据, 红线也会慢慢被拉直, 变得没那么扭曲 .

  1. 从数据源头获得更多数据:多拍点照片等。

  2. 数据增强(data augmentation):通过一定规则来扩充数据,比如旋转,平移,亮度,切割等手段一张图片得到多张。

提前停止(Early stopping)

它通过在训练过程中监控验证集上的性能,并在性能开始下降时停止训练,来防止模型过度拟合训练数据。这种方法有效地控制了模型的复杂度,使其不会在训练数据上过分拟合,从而提高了模型的泛化能力。

2.BN和LN

BN 和 LN 都是用来解决神经网络中的梯度消失和爆炸问题的方法。BN(批量归一化)是在每一层的输入上进行归一化,而 LN(层归一化)是在每个样本的输入上进行归一化。它们的作用都是将输入数据进行归一化处理,有助于加速训练过程并提高模型的泛化能力。

区别

BN(批量归一化)和LN(层归一化)在神经网络中都是用来解决梯度消失和梯度爆炸问题的方法,但它们之间有一些区别:

  1. 作用对象:BN是在每一层的输入上进行归一化,即对每层的输出进行归一化处理;而LN是在每个样本的输入上进行归一化,即对每个样本的输出进行归一化处理。
  2. 归一化方式:BN是对每一层的输出进行归一化,通过计算每个特征在整个批次上的均值和方差来实现归一化;而LN是对每个样本的输出进行归一化,通过计算每个样本在所有特征上的均值和方差来实现归一化。
  3. 应用场景:BN通常应用于深层神经网络中,可以加速训练过程并提高模型的泛化能力;而LN通常应用于循环神经网络(RNN)等结构中,可以提高模型对序列数据的建模能力。

总体来说,BN和LN都是用来解决梯度消失和梯度爆炸问题的有效方法,但在具体应用时需要根据网络结构和任务特点选择合适的归一化方法。

3.介绍注意力机制

注意力机制是一种用于增强神经网络性能的技术,它模拟人类注意力的行为,使网络能够在处理输入数据时更加关注重要的部分。在注意力机制中,网络学会了动态地分配不同权重给输入的不同部分,以便更好地处理信息。

具体来说,注意力机制通常包括三个关键组件:

  1. 查询(Query): 查询是用来获取对输入的关注程度的向量。它可以看作是网络要查询的信息。
  2. 键(Key)和值(Value): 键和值是与输入数据相关联的向量。键向量用于计算注意力权重,而值向量则是要被加权的输入数据。
  3. 注意力权重(Attention Weights): 注意力权重是由查询和键计算得到的权重向量,用来指示网络对值的关注程度。这些权重确定了输入中每个部分对最终输出的影响程度。

通过计算查询与键之间的相似度,然后将相似度转换为注意力权重,注意力机制能够使网络在处理输入数据时自动地学习到哪些部分是最重要的。这种机制已被成功地应用于各种任务,如机器翻译、图像标注和自然语言处理等领域,取得了显著的性能提升。