面试理想汽车,搞砸了。。。

文摘   2024-11-26 18:16   北京  

你好,我是kk~

这几天有同学面试了理想汽车,在问到 ResNet 关于论文和原理的时候,吞吞吐吐,也许是因为这个,最后面试搞砸了。

那么,今天咱们也来和大家聊聊ResNet。

idea

首先,来和大家聊聊ResNet 作为论文的 Idea 的建议

以 ResNet(Residual Neural Network)为基础,可以有以下几种研究和改进方向来撰写论文:

1. 理论分析与扩展

研究 ResNet 的理论性质,解释其成功的原因,或者推导它的数学性质。

  • 深层网络优化的梯度消失问题:分析 ResNet 如何通过 residual connection 改善梯度传播,结合数学推导和实验对比。
  • 层级学习能力:讨论 ResNet 的 skip connection 是否导致网络倾向于学习浅层特征。
  • 路径长度理论:通过公式推导解释 ResNet 提升性能的背后原因,比如路径长度的多样性和模型复杂度。

2. 结构改进

基于 ResNet 提出新的变体,解决特定领域问题。

  • 动态权重残差块:为残差块引入动态权重。
  • 轻量化优化:研究在移动端或者嵌入式环境下的 ResNet 结构改进。
  • 结合其他机制:例如将注意力机制(Attention)与 ResNet 结合。

3. 应用拓展

在新领域或者数据类型上验证和优化 ResNet。

  • 自然语言处理:探索 ResNet 结构在 Transformer 或 RNN 等 NLP 模型中的应用。
  • 时序数据:利用 ResNet 改进时序数据分析任务的性能。
  • 生成模型:研究 ResNet 在生成对抗网络(GANs)中的性能改进。

4. 可解释性与鲁棒性

研究 ResNet 的可解释性和鲁棒性,提升其在实际应用中的可靠性。

  • 残差路径的可解释性:分析每条 residual connection 的特征贡献。
  • 对抗鲁棒性研究:探讨 ResNet 在 adversarial attacks 下的表现并优化。

深入原理

ResNet 的核心思想是引入残差(Residual)连接,通过公式推导如下:

1. 经典深层网络的问题

在普通深层网络中,假设输入为 ,输出为 ,每一层的映射函数为 ,则输出可表示为:

其中  是网络参数。

深层网络由于梯度传播路径长,容易出现梯度消失或爆炸。

2. ResNet 的 Residual Mapping

ResNet 引入了 shortcut connection,使每一层的输入不仅通过映射 ,还可以直接加到输出上:

其中:

  •  是残差函数(通常为卷积层)。
  •  是输入通过 shortcut 直接加入输出。

3. 优势分析

通过这种设计:

  • 原始输入  可以直接传递到更深的层,缓解梯度消失。
  • 网络优化的目标变为学习残差 ,而不是直接拟合 ,使得学习任务更加简单。

4. 梯度传递公式

假设损失函数为 ,则梯度为:

这表明梯度可以通过  的直接路径稳定地传递到浅层,缓解梯度消失。

一个案例

用 ResNet 训练 CIFAR-10 数据集并可视化

下面是一个基于 PyTorch 的简化实现,帮助大家理解 ResNet 的工作原理。

import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import ssl

ssl._create_default_https_context = ssl._create_unverified_context

# 定义一个简单的 ResNet 模块
class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, stride=1):
        super(ResidualBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)
        
        # Shortcut connection
        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride),
                nn.BatchNorm2d(out_channels)
            )
    
    def forward(self, x):
        out = torch.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        return torch.relu(out)

# 定义 ResNet 架构
class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_channels = 64
        self.conv1 = nn.Conv2d(364, kernel_size=3, stride=1, padding=1)
        self.bn1 = nn.BatchNorm2d(64)
        self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
        self.fc = nn.Linear(256, num_classes)
    
    def _make_layer(self, block, out_channels, num_blocks, stride):
        strides = [stride] + [1] * (num_blocks - 1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_channels, out_channels, stride))
            self.in_channels = out_channels
        return nn.Sequential(*layers)
    
    def forward(self, x):
        out = torch.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = torch.mean(out, dim=[23])  # Global Average Pooling
        out = self.fc(out)
        return out

# 准备 CIFAR-10 数据
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.50.50.5), (0.50.50.5))
])

trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False)

# 初始化模型和训练
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = ResNet(ResidualBlock, [222]).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
def train_model():
    for epoch in range(5):
        model.train()
        running_loss = 0.0
        for inputs, labels in trainloader:
            inputs, labels = inputs.to(device), labels.to(device)
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
        print(f"Epoch {epoch+1}, Loss: {running_loss/len(trainloader):.4f}")

train_model()

# 可视化残差块特征
def visualize_features():
    model.eval()
    with torch.no_grad():
        dataiter = iter(testloader)
        images, labels = next(dataiter)
        images = images.to(device)
        
        # 通过第一层残差块
        features = model.layer1[0](images).cpu()
        fig, axes = plt.subplots(15, figsize=(155))
        for i in range(5):
            axes[i].imshow(features[0, i].detach().numpy(), cmap='viridis')
            axes[i].axis('off')
        plt.show()

visualize_features()

其中:

  1. ResidualBlock 定义了一个标准的 ResNet 残差块。
  2. ResNet 使用了多个残差块,支持简单的 CIFAR-10 分类任务。
  3. 训练部分:展示了如何在 CIFAR-10 数据集上训练模型。
  4. 可视化部分:展示残差块提取的特征图,帮助初学者理解 ResNet 的作用。

kk机器学习算法
机器学习基础、计算机视觉…
 最新文章