自编码器--无监督学习
发布时间
阅读量:
阅读量
自编码器
无监督学习的神经网络模型,主要用于特征提取、数据降维、数据去噪等。
目标是:通过压缩输入数据并将其重建,逼近输入数据的原始形态。主要学习输入数据的核心特征。
结构:
- 编码器:负责将输入数据映射到一个低维的潜在空间表示(latent space)。它的作用是提取输入数据的核心特征,同时尽可能减少信息损失。
- 解码器:负责从潜在空间的表示中重建输入数据。解码器试图将低维的特征表示还原成与原始数据尽可能接近的形式。
整个网络通过最小化重建误差来优化。
工作原理:
自编码器的训练目标是让输入 x 和重建的输出
尽可能相似。
- 编码阶段:输入x被编码器压缩到一个低维表示h:h=f(Wx+b)。W 是权重矩阵,b是偏置向量,f(⋅) 是激活函数。
- 解码阶段:解码器接收编码后的表示 h,并通过反向映射来重建输入
:
=g(W′h+b′)。W′ 和 b′ 是解码器的权重和偏置,g(⋅) 是解码器的激活函数。 - 损失函数:输入 x与重建后的
之间的重建误差 。常见的损失函数是均方误差(Mean Squared Error, MSE) :

自编码器通过最小化这个重建误差来学习输入数据的特征。
实例:
1.手写数字图片像素压缩
实现自编码器对手写数据集MNIST(2828 像素的灰度图像)进行特征学习。这个数据集的每个样本都是一个 2828 的手写数字图片,表示从 0 到 9 的数字。目标是训练一个自编码器,将这些图片压缩到低维空间,然后再重建它们。
- 输入数据:28*28像素的灰度图像,展开为一行,784维的向量。
- 编码器压缩(例64维)。W为64*784的权重矩阵。
- 解码器重建:解码器接受低维表示,尝试还原为原始数据(28*28)
- 损失函数:

n:输入维度(这里n=784),
是输入数据第i个像素的值,
是重建后的第i个像素的值。
最简单的自编码器实现数据特征提取、压缩。两层神经网络:
输入层到隐藏层用来编码,隐藏层到输出层用来解码,层与层之间互相全连接。
import torch
import torch.nn as nn
import torch.optim as optim
form torchvision import datasets,transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
# 定义自编码器模型
class Autoencoder(nn.Module):
def __init__(self):
super(Autoencoder, self).__init__()
# 编码器
self.encoder = nn.Sequential(
nn.Linear(784, 64), # 输入层到64维编码层
nn.ReLU()
)
# 解码器
self.decoder = nn.Sequential(
nn.Linear(64, 784), # 64维编码到784维重建层
nn.Sigmoid()
)
def forward(self, x):
x = self.encoder(x)
x = self.decoder(x)
return x
# 加载MNIST数据集
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
test_dataset = datasets.MNIST(root='./data', train=False, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=256, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=256, shuffle=False)
# 实例化模型,损失函数,和优化器
model = Autoencoder()
criterion = nn.MSELoss() # 使用均方误差
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 训练自编码器
epochs = 50
for epoch in range(epochs):
for data in train_loader:
img, _ = data # 忽略标签
img = img.view(img.size(0), -1) # 展平成 784 维
# 前向传播
output = model(img)
loss = criterion(output, img)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f'Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}')
# 使用测试集重建图像
model.eval()
with torch.no_grad():
for data in test_loader:
img, _ = data
img = img.view(img.size(0), -1)
output = model(img)
break # 只显示一批数据的结果
# 展示原始图像和重建图像
n = 10
plt.figure(figsize=(20, 4))
for i in range(n):
# 原始图像
ax = plt.subplot(2, n, i + 1)
plt.imshow(img[i].view(28, 28).numpy(), cmap='gray')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# 重建图像
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(output[i].view(28, 28).numpy(), cmap='gray')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
起初疑问:
解码器会拿压缩降维后的数据进行原始数据重建,那哪里起到降维的作用了呢???
降维:编码阶段。解码器是在保证编码器提取的特征有用且有意义,是所需要的可以描述原始数据的特征。如果验证有效,便使用编码阶段的输出,即64维的数据。
课外联想:降维
主成分分析(PCA):无监督学习方法,利用正交变换把由线性相关表示的观测数据转换为几个线性不想关变量表示的数据。
而自编码器可以处理非线性的数据进行降维。
2.自编码器对海洋哺乳动物叫声数据进行特征提取与降噪重构
自编码器还可以消除叫声中的噪声,重建出更干净的叫声数据(解码器部分)。
编码器:提取重要特征,去除噪声。
解码器:重构使用的是编码器的数据,不包含噪声,重构出和原始数据近似的数据。
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
# 假设叫声数据为频谱图格式,大小为 64x64
input_shape = (1, 64, 64)
# 定义自编码器结构
class DenoisingAutoencoder(nn.Module):
def __init__(self):
super(DenoisingAutoencoder, self).__init__()
# 编码器部分
self.encoder = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1), # 输出形状: (32, 64, 64)
nn.ReLU(True),
nn.MaxPool2d(2, stride=2, padding=0), # 输出形状: (32, 32, 32)
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1), # 输出形状: (64, 32, 32)
nn.ReLU(True),
nn.MaxPool2d(2, stride=2, padding=0), # 输出形状: (64, 16, 16)
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),# 输出形状: (128, 16, 16)
nn.ReLU(True),
nn.MaxPool2d(2, stride=2, padding=0) # 输出形状: (128, 8, 8)
)
# 解码器部分
self.decoder = nn.Sequential(
nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2, padding=1, output_padding=1), # (64, 16, 16)
nn.ReLU(True),
nn.ConvTranspose2d(64, 32, kernel_size=3, stride=2, padding=1, output_padding=1), # (32, 32, 32)
nn.ReLU(True),
nn.ConvTranspose2d(32, 1, kernel_size=3, stride=2, padding=1, output_padding=1), # (1, 64, 64)
nn.Sigmoid()
)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return encoded, decoded
# 初始化模型、损失函数和优化器
model = DenoisingAutoencoder()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 生成随机的带噪声和无噪声数据(用来模拟叫声数据)
x_train = np.random.rand(100, 1, 64, 64).astype(np.float32) # 无噪声数据
x_train_noisy = x_train + 0.5 * np.random.normal(size=x_train.shape).astype(np.float32)
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
# 转换为 PyTorch 张量
x_train = torch.tensor(x_train)
x_train_noisy = torch.tensor(x_train_noisy)
# 训练自编码器
num_epochs = 20
for epoch in range(num_epochs):
model.train()
optimizer.zero_grad()
_, outputs = model(x_train_noisy)
loss = criterion(outputs, x_train) # 将去噪后的重构与无噪声的原始数据进行对比
loss.backward()
optimizer.step()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 可视化原始的、带噪声的和重建的频谱图
model.eval()
with torch.no_grad():
encoded_imgs, decoded_imgs = model(x_train_noisy)
n = 5 # 展示5张图像
plt.figure(figsize=(10, 6))
for i in range(n):
# 原始图像
ax = plt.subplot(3, n, i + 1)
plt.imshow(x_train[i].squeeze(), cmap="gray")
plt.title("Original")
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# 带噪声图像
ax = plt.subplot(3, n, i + 1 + n)
plt.imshow(x_train_noisy[i].squeeze(), cmap="gray")
plt.title("Noisy")
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# 重建后的图像
ax = plt.subplot(3, n, i + 1 + 2 * n)
plt.imshow(decoded_imgs[i].squeeze(), cmap="gray")
plt.title("Denoised")
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
反卷积(转置卷积)
空间维度是将低维升到高维,和卷积相反。类似于上采样,池化层相反作用。
自编码器中常用于重构原始数据。
反卷积的计算过程:

全部评论 (0)
还没有任何评论哟~
