深度学习(15)从头搭建模型到训练、预测示例总结
- 总结一下从头搭建模型的流程
- 再次熟悉一下模型结构与训练、推理流程
- 本文主要介绍通过
Pytorch
实现模型结构实现
1.价格预测
要搭建一个深度学习网络模型用于价格预测,首先需要明确问题的类型(回归问题)以及输入数据的特点。假设你的目标是预测某种商品或房产等的价格,常用的方法是构建一个全连接神经网络(Feedforward Neural Network)。以下是搭建一个基本回归模型的步骤:
-
- 数据准备
特征工程:收集并处理数据。通常包括数值型特征(如面积、数量、时间等)和类别型特征(如地区、类型等),并对类别特征进行独热编码。
数据划分:将数据集划分为训练集和验证集。
- 数据准备
-
- 网络结构设计
输入层:每个输入特征对应一个神经元。
隐藏层:通常可以使用2-3层隐藏层,每层包含几十到几百个神经元。激活函数使用ReLU(Rectified Linear Unit)。
输出层:由于是回归任务,输出层使用一个神经元,不应用激活函数(或者使用线性激活函数),用于预测价格
- 网络结构设计
-
3.PyTorch 提供了更低级的控制,允许你定制化网络结构、训练过程和优化策略。下面是一个使用 PyTorch 实现的简单全连接神经网络(Feedforward Neural Network, FNN)来解决回归任务(价格预测)的问题。
-
4.准备工作,安装所需的库:
pip install torch torchvision scikit-learn
- 代码实现
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np
# 假设 X 是特征数据, y 是价格标签
# 这里我们使用随机生成数据作为示例
# 你需要替换为实际的数据
X = np.random.rand(1000, 10) # 1000个样本,10个特征
y = np.random.rand(1000, 1) # 1000个样本,目标价格
# 数据标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 划分数据集
X_train, X_val, y_train, y_val = train_test_split(X_scaled, y, test_size=0.2, random_state=42)
# 转换为PyTorch张量
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32)
X_val_tensor = torch.tensor(X_val, dtype=torch.float32)
y_val_tensor = torch.tensor(y_val, dtype=torch.float32)
# 定义神经网络模型
class PricePredictor(nn.Module):
def __init__(self, input_dim):
super(PricePredictor, self).__init__()
self.fc1 = nn.Linear(input_dim, 128) # 输入层到隐藏层1
self.fc2 = nn.Linear(128, 64) # 隐藏层1到隐藏层2
self.fc3 = nn.Linear(64, 32) # 隐藏层2到隐藏层3
self.fc4 = nn.Linear(32, 1) # 隐藏层3到输出层
def forward(self, x):
x = torch.relu(self.fc1(x)) # ReLU 激活函数
x = torch.relu(self.fc2(x))
x = torch.relu(self.fc3(x))
x = self.fc4(x) # 输出层,不需要激活函数
return x
# 实例化模型
input_dim = X_train.shape[1] # 输入维度(特征数)
model = PricePredictor(input_dim)
# 定义损失函数和优化器
criterion = nn.MSELoss() # 均方误差损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
num_epochs = 100
batch_size = 32
for epoch in range(num_epochs):
model.train()
# 打乱数据
indices = torch.randperm(X_train_tensor.size(0))
X_train_tensor = X_train_tensor[indices]
y_train_tensor = y_train_tensor[indices]
# 按批次训练
for i in range(0, X_train_tensor.size(0), batch_size):
# 获取当前批次数据
X_batch = X_train_tensor[i:i+batch_size]
y_batch = y_train_tensor[i:i+batch_size]
# 前向传播
outputs = model(X_batch)
loss = criterion(outputs, y_batch)
# 反向传播和优化
optimizer.zero_grad() # 清除之前的梯度
loss.backward() # 反向传播
optimizer.step() # 更新模型参数
# 每隔一定周期打印训练损失
if (epoch + 1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
# 验证模型
model.eval()
with torch.no_grad(): # 不需要计算梯度
val_outputs = model(X_val_tensor)
val_loss = criterion(val_outputs, y_val_tensor)
print(f'Validation Loss: {val_loss.item():.4f}')
# 使用模型进行预测
model.eval()
with torch.no_grad():
predictions = model(X_val_tensor)
print("Predictions:", predictions[:5].numpy()) # 打印前5个预测结果
-
- 代码解析
- 数据处理:
StandardScaler 用于标准化数据,使其均值为0,方差为1,有助于加速训练。数据集被划分为训练集和验证集,使用 train_test_split 进行划分。 - 神经网络定义:
PricePredictor 类继承自 nn.Module,定义了一个简单的全连接网络(3个隐藏层)。每个隐藏层使用 ReLU 激活函数。输出层是一个线性层,用于回归任务,不使用激活函数。 - 训练过程:
采用 Adam 优化器,学习率设置为 0.001。损失函数使用 均方误差(MSE),适用于回归任务。使用小批量(batch)梯度下降来训练网络。 - 评估模型:
在验证集上计算损失,评估模型的性能。 - 预测:
使用训练好的模型进行预测,输出预测值。
2. 图像分类
-
要使用 PyTorch 搭建一个用于图像分类的深度学习模型,我们可以构建一个典型的卷积神经网络(CNN)。CNN 在图像分类任务中表现出色,因为它能够有效地捕捉图像的空间结构特征。
-
这里将展示如何使用 PyTorch 创建一个简单的卷积神经网络来进行图像分类。假设你已经有一个图像数据集,并且任务是对图像进行分类。以下是一个简单的 CNN 模型,用于处理图像分类任务。
- 安装 PyTorch
首先,确保你已经安装了 PyTorch。如果没有安装,可以运行以下命令:
pip install torch torchvision
-
构建 CNN 模型
我们将使用 PyTorch 构建一个简单的卷积神经网络模型。这个模型包含多个卷积层(Convolutional Layer)、池化层(Pooling Layer)、全连接层(Fully Connected Layer),最终输出预测类别。 -
PyTorch 示例代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
from sklearn.metrics import accuracy_score
# 数据预处理和增强
transform = transforms.Compose([
transforms.Resize((128, 128)), # 缩放图像到 128x128
transforms.ToTensor(), # 转换为 Tensor 格式
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 图像归一化
])
# 加载训练和验证数据集
train_dataset = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
val_dataset = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# 定义 CNN 模型
class SimpleCNN(nn.Module):
def __init__(self, num_classes=10):
super(SimpleCNN, self).__init__()
# 卷积层
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
# 池化层
self.pool = nn.MaxPool2d(2, 2)
# 全连接层
self.fc1 = nn.Linear(128 * 16 * 16, 512) # 输入图像大小: 128x128, 卷积后大小: 16x16
self.fc2 = nn.Linear(512, num_classes)
def forward(self, x):
# 卷积 -> 激活 -> 池化
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = self.pool(torch.relu(self.conv3(x)))
# 展平
x = x.view(-1, 128 * 16 * 16)
# 全连接层
x = torch.relu(self.fc1(x))
x = self.fc2(x) # 输出类别
return x
# 实例化模型
model = SimpleCNN(num_classes=10) # CIFAR-10有10个类别
# 损失函数和优化器
criterion = nn.CrossEntropyLoss() # 多分类交叉熵损失
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练模型
num_epochs = 10
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
correct = 0
total = 0
for images, labels in train_loader:
# 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
# 计算准确率
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# 计算训练集的损失和准确率
train_loss = running_loss / len(train_loader)
train_accuracy = 100 * correct / total
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {train_loss:.4f}, Accuracy: {train_accuracy:.2f}%')
# 验证模型
model.eval()
correct = 0
total = 0
with torch.no_grad():
for images, labels in val_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
val_accuracy = 100 * correct / total
print(f'Validation Accuracy: {val_accuracy:.2f}%')
# 保存模型
torch.save(model.state_dict(), 'cnn_model.pth')
- 代码解释
-
数据预处理:
- 使用 transforms.Compose() 来进行图像处理:调整图像大小、转换为 Tensor 格式、归一化(标准化)图像。
- CIFAR-10 数据集包含 10 类图片,每个图片是 32x32 的彩色图像,因此我们将图像尺寸调整为 128x128。
-
CNN 模型:
- SimpleCNN 类是模型的核心,包含了 3 个卷积层,每个卷积层后面跟随一个 ReLU 激活函数和池化层(MaxPooling)。
- 最后是两个全连接层(fc1 和 fc2),用于将卷积层提取的特征映射到最终的类别输出。
-
训练过程:
- 使用 Adam 优化器和 CrossEntropyLoss 损失函数,这两个是分类任务中常用的设置。
- 训练过程包括正向传播、计算损失、反向传播、参数更新等步骤。
每训练一个 epoch,会输出当前的损失和训练准确率。
-
验证过程:
在验证集上评估模型的准确率。使用 torch.no_grad() 来避免在验证过程中计算梯度。
3. 图像分割
-
图像分割(Image Segmentation)是计算机视觉中的一个任务,目标是将图像分成若干区域,其中每个区域代表一个对象或者场景的某一部分。常见的图像分割任务包括语义分割(Semantic Segmentation)和实例分割(Instance Segmentation)。
-
在下文中中,我们将使用 U-Net 网络进行图像分割。U-Net 是一种常见且有效的卷积神经网络架构,特别适用于医学图像分割任务,但也适用于其他场景。它由编码器(卷积层和池化层)和解码器(上采样和卷积层)组成,并且通过跳跃连接(Skip Connections)连接编码器和解码器,以帮助恢复空间信息。
- 安装 PyTorch 和 torchvision
首先,确保你已经安装了 PyTorch 和 torchvision。你可以使用以下命令安装:
pip install torch torchvision
-
构建 U-Net 模型
我们将从头开始构建 U-Net 模型。U-Net 主要包括以下几个部分:- 编码器:由多个卷积层和池化层组成,用于提取图像特征。
- 解码器:由多个卷积层和上采样层组成,用于恢复图像的空间分辨率。
- 跳跃连接:用于将编码器中的特征传递到解码器中,帮助恢复细节信息。
-
PyTorch 代码示例:U-Net 图像分割模型
3.1 导入必要的库
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import numpy as np
import matplotlib.pyplot as plt
3.2 定义 U-Net 模型
class UNet(nn.Module):
def __init__(self, in_channels, out_channels):
super(UNet, self).__init__()
# 编码器部分
self.encoder1 = self.conv_block(in_channels, 64)
self.encoder2 = self.conv_block(64, 128)
self.encoder3 = self.conv_block(128, 256)
self.encoder4 = self.conv_block(256, 512)
# 中间部分
self.center = self.conv_block(512, 1024)
# 解码器部分
self.decoder4 = self.upconv_block(1024, 512)
self.decoder3 = self.upconv_block(512, 256)
self.decoder2 = self.upconv_block(256, 128)
self.decoder1 = self.upconv_block(128, 64)
# 输出层
self.output = nn.Conv2d(64, out_channels, kernel_size=1)
def conv_block(self, in_channels, out_channels):
return nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
nn.ReLU(inplace=True)
)
def upconv_block(self, in_channels, out_channels):
return nn.Sequential(
nn.ConvTranspose2d(in_channels, out_channels, kernel_size=2, stride=2),
nn.ReLU(inplace=True)
)
def forward(self, x):
# 编码器部分
enc1 = self.encoder1(x)
enc2 = self.encoder2(enc1)
enc3 = self.encoder3(enc2)
enc4 = self.encoder4(enc3)
# 中间部分
center = self.center(enc4)
# 解码器部分
dec4 = self.decoder4(center)
dec3 = self.decoder3(dec4)
dec2 = self.decoder2(dec3)
dec1 = self.decoder1(dec2)
# 输出层
out = self.output(dec1)
return out
3.3 训练和数据加载
这里我们假设你有一个图像分割数据集,并且标注为二分类(比如背景和前景)。我们将使用数据集的图像和标签来训练模型。
# 假设你有一个自定义数据集,这里使用PIL图像和Tensor转换为例
class SegmentationDataset(torch.utils.data.Dataset):
def __init__(self, images, masks, transform=None):
self.images = images
self.masks = masks
self.transform = transform
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
image = self.images[idx]
mask = self.masks[idx]
if self.transform:
image = self.transform(image)
mask = self.transform(mask)
return image, mask
# 数据增强和转换
transform = transforms.Compose([
transforms.ToTensor(),
])
# 假设 images 和 masks 已经加载到内存中,并且是正确格式的
# images: 一个形状为 (N, H, W, C) 的 numpy 数组
# masks: 一个形状为 (N, H, W) 的 numpy 数组
train_dataset = SegmentationDataset(images, masks, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=4, shuffle=True)
# 创建模型
model = UNet(in_channels=3, out_channels=1) # 3 输入通道(RGB图像),1 输出通道(分割掩码)
# 损失函数和优化器
criterion = nn.BCEWithLogitsLoss() # 对于二分类使用 BCEWithLogitsLoss
optimizer = optim.Adam(model.parameters(), lr=1e-4)
# 训练模型
num_epochs = 10
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for images, masks in train_loader:
# 将图像和标签放到设备上
images = images.to(device)
masks = masks.to(device)
# 前向传播
outputs = model(images)
loss = criterion(outputs.squeeze(1), masks)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')
3.4 测试和评估
测试和评估部分是通过模型推理(inference)来获取分割结果。
# 测试部分
model.eval()
with torch.no_grad():
for images, masks in test_loader:
images = images.to(device)
masks = masks.to(device)
# 推理
outputs = model(images)
predicted = torch.sigmoid(outputs) > 0.5 # 将输出映射到 [0, 1] 范围,0.5为阈值
# 可以进行后处理(例如计算IoU, Dice系数等)
# 显示结果或保存图像
plt.imshow(predicted[0].cpu().numpy(), cmap='gray')
plt.show()
- 进一步改进
- 数据增强:在训练过程中可以进行数据增强,例如随机裁剪、旋转、翻转等,以增加模型的鲁棒性。
- 多通道输出:如果任务是多类分割,模型的输出通道可以设置为类别数,每个像素点有多个通道表示其属于不同类别的概率。
- 预训练模型:可以使用基于深度卷积神经网络(如ResNet)的预训练模型来初始化网络的编码器部分,这通常有助于加速训练过程。