相信大家看了NLP方向的题目之后都被出题人的善良耐心认真所折服😀🎉🎉 接下来,欢迎大家跟我一起走进CV的世界😋👍
写在前面的话
"随着自然语言处理技术的飞速发展,计算机视觉领域迎来了新的变革。🌀 大语言模型的出现,不仅推动了机器理解人类语言的能力,也为计算机视觉系统提供了新的视角。👀 这些模型能够将图像内容与语言描述紧密结合,使得机器能够以前所未有的方式 “看到” 并 “讲述” 视觉故事。📖 这种跨学科的融合,为图像识别、场景理解以及视觉问答等领域开辟了新的可能性,预示着人工智能技术的下一个前沿。✨
(上面那段话是KIMI老师帮我写的)
数据集(此处是本题用会到的全部数据集):
大家需要自行将数据集划分为训练、测试以及验证集。
由于数据集大小限制,大家的测试集与验证集每一个类别仅包含5张图片即可
📝注意事项
在本题中出题人并未给大家推荐任何学习资料或网站,希望大家可以熟练运用B站、Google、CSDN、知乎、AI工具(GPT、Kimi、智谱清言···)等多种信息检索渠道来对知识点进行学习
想要成为扑克高手吗
想要成为扑克高手吗?回想我初学扑克牌时的情景,我发现我常常苦恼于Card种类繁杂,无法记住所有的Card,而在我学习了CV知识后,便心生一计☝️🤓,准备做一个识别各种Card的模型,当遇到有不认识的Card时,只需要将图片扔给模型,就能知道这张Card是什么。
Part 01:前置知识
本题主要是帮助大家打牢机器学习的基础,为后续题目的完成做铺垫~
🛠️ 环境、基础相关
-
使用PyTorch深度学习框架:统一要求使用PyTorch深度学习框架,且有独立显卡的要求安装GPU版本。
-
Tensor张量:了解什么是Tensor张量,以及其与numpy数组的区别。
-
图像的通道数:理解什么是图像的通道数,以及RGB在图像的储存中代表什么,RGB三个通道值的范围是什么。
-
图像转换:学习如何将图像转换成神经网络能读懂的数据。
-
输入图像处理:探索一般如何处理输入图像的通道。
-
数据集加载:了解如何使用Pytorch加载数据集。
-
One-Hot编码:理解One-Hot编码的含义。
-
······
🧠 ML基础相关
-
正则化:了解正则化是什么,以及L1、L2正则化的概念。
-
归一化:掌握归一化的概念及其重要性。
-
激活函数:理解激活函数是什么,以及其在神经网络中的作用。
-
多层感知机MLP:学习什么是多层感知机MLP。
-
梯度下降算法:了解梯度下降算法是什么,反向传播算法又是什么,以及梯度下降的原理。
-
过拟合与欠拟合:了解什么是过拟合、欠拟合。
-
梯度消失与梯度爆炸:简单了解梯度消失与梯度爆炸的出现情况及其解决方案。
-
卷积基础:理解卷积是什么,卷积核、步长是什么,以及感受野和池化的概念。
-
卷积神经网络:了解一个卷积神经网络一般可以分为哪几层。
-
池化类型:探索池化有哪些类型,其作用分别有什么。
-
激活层作用:理解激活层在此的作用,以及卷积神经网络的输出是什么样的。
-
······
提交要求
- 请完成题目中所提到的知识点的学习笔记
请以 Markdown 格式编写笔记,并将笔记导出为PDF格式提交
Part 02 数据预处理与数据增强
Little Tips:在炼丹过程中,数据预处理的效果可能直接决定了模型的准确度。我们从最简单的数据预处理方法开始,逐步探索对图像数据进行处理的方法。
👀 任务列表
-
肉眼观察数据集:将离群数据清除。
-
数据集统计:对数据集内的图像信息做统计(包括但不仅限于:尺寸信息、格式信息···)。
-
数据集划分:完成训练集、测试集以及验证集的划分。
-
数据增强学习:学习对图像常见的数据增强方式。
-
数据增强可视化:编写代码将数据增强后的图像可视化。
-
图像尺寸调整:在输入网络前记得调整图像尺寸以匹配网络。
-
数据增强策略:思考在我们的训练过程中,数据增强应该对哪些/哪一个数据集使用(注意我们的核心目标)。
-
图像分类问题:思考,我们如何将扑克牌识别问题转换成图像分类问题来处理?
-
······
在使用了数据增强的训练过程中,可以打印出每一个epoch之后(甚至在每10个batch之后打印都行,因为我就是这么干的),模型在训练集上的准确率与验证集上的准确率,并对二者的变化趋势进行观察。
提交要求
请以 Markdown 格式编写笔记,并将笔记导出为PDF格式提交
- 请完成题目中所提到的知识点的学习笔记
Part 03 看看👀效果
在本题中,我为大家会提供预先训练好的参数稍大的模型及其权重参数。
大家要做的就是参考并学习出题人给出的代码(代码在出题人的环境下恰好能跑,所以想偷懒的出题人便直接原封不动上传了),同时根据报错信息或直接对代码进行debug,找出代码不能在各位的电脑上运行的原因,并对bug进行修复,使其在你们自己的电脑上运行起来。
代码
import torch
import cv2
import pandas as pd
from torchvision import transforms
import torch
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from torch.utils.data import Dataset
from PIL import Image
from torch.utils.data import DataLoader
from torch import nn
import torch.optim as optim
import torch.nn.init as init
import torch.nn.functional as F
import os
from tqdm import tqdm
transform_test = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(), # 将图像转换为Tensor
'''
在这里添加数据增强操作···
'''
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])
testset = datasets.ImageFolder(root='./test', transform=transform_test)
test_loader = DataLoader(testset, batch_size=32, shuffle=False)
class Tiny_cnn(nn.Module):
def __init__(self, num_classes=53):
super(Tiny_cnn, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size = 8 , stride=4, padding=2),
nn.ReLU(inplace=True),
nn.BatchNorm2d(64),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(64, 192, kernel_size=5, padding=2),
nn.ReLU(inplace=True),
nn.BatchNorm2d(192),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(192, 384, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.BatchNorm2d(384),
nn.Conv2d(384, 256, kernel_size=2, padding=2),
nn.ReLU(inplace=True),
nn.BatchNorm2d(256),
nn.MaxPool2d(kernel_size=3, stride=2)
)
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256 * 8 * 8, 4096),
nn.ReLU(inplace=True),
nn.Dropout(),
nn.Linear(4096, 1024),
nn.ReLU(inplace=True),
nn.Linear(1024, num_classes)
)
def forward(self, x):
x = self.features(x)
# print(x.shape)
x = torch.flatten(x, 1)
# print(x.shape)
x = self.classifier(x)
# print(x.shape)
return x
net = Tiny_cnn() # 替换为你的模型
# 定义模型保存路径
PATH = 'tiny_cnn.pt'
# net.to('cuda')
state_dict = torch.load(PATH, map_location='cuda')
if os.path.exists(PATH):
print("加载模型...")
model = torch.load(PATH, map_location='cuda')
from collections import OrderedDict
new_state_dict = OrderedDict()
print(state_dict.keys())
for k, v in model.state_dict().items():
name = k[7:] # remove `module.`
new_state_dict[name] = v
# load params
net.load_state_dict(new_state_dict)
# net.load_state_dict(model.state_dict())
net.to('cuda')
# 使用 nn.DataParallel 包装模型以在多个 GPU 上训练
if torch.cuda.device_count() >= 1: # 注意,这里改为>=1
print(f"使用 {torch.cuda.device_count()} 个 GPU 进行训练")
net = nn.DataParallel(net)
# 评估模式
net.eval()
# 测试过程
correct = 0
total = 0
with torch.no_grad(): # 禁用梯度计算
test_iterator = tqdm(test_loader, desc="Testing")
for images, labels in test_iterator:
images, labels = images.to('cuda'), labels.to('cuda')
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
# 更新进度条
test_iterator.set_postfix(accuracy=(correct / total))
print(f'Accuracy of the network on the test images: {100 * correct / total:.2f}%')
else:
print("模型文件不存在")
注意,以上代码的运行要求为:GPU数量≥1。若只有CPU环境,还请大家自行修改代码以适配环境(其实就是改几处小地方~)
需要自行下载的文件
注意,models压缩包在此处存在的原因是出题人在保存权重参数时弄出的小bug🤐。大家需要将models文件夹解压后放在与上面的运行代码相同的父文件夹中~~
提交要求
-
记录下大家遇到的大大小小bug,及其相应的解决方案
-
尝试解释出题人给出的代码流程以及对细节的理解
-
将运行成功截图添加至文档中
Part 04 动手搭建
Obviously,上一题中出题人给出的网络架构有些复杂。让大家在自己的GPU甚至CPU(不过我们明确要求笔记本上有独立显卡条件的一定要安装cuda版本的torch)上训练是不太现实的。
因此在本题中,我们引导大家根据下面列出的模板自行构建一个神经网络(其中网络的深度、CNN与Linear的层数、卷积核大小、通道数等参数的设置各位均可自由发挥,以下模板仅供参考)
训练流程,数据集加载等就不赘述了。各位可以根据上一Part中的代码及逆行修改。
class MicroCNN(nn.Module):
def __init__(self, num_classes=53):
super(MicroCNN, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1), # 输出尺寸: (16, 224, 224)
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2), # 输出尺寸: (16, 112, 112)
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), # 输出尺寸: (32, 112, 112)
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2) # 输出尺寸: (32, 56, 56)
)
self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(32 * 56 * 56, 256), # 减少到256个神经元
nn.ReLU(inplace=True),
nn.Linear(256, num_classes)
)
def forward(self, x):
x = self.features(x)
x = torch.flatten(x, 1)
x = self.classifier(x)
return x
大家可以将epoch设置为50以内(如果大家的GPU算力比较足的话可以适当开高一些),并且看看效果😀
提交要求
-
解释代码流程
-
记录自己修改网络架构的历史(如:参数变化、准确率变化、优化器选择······)
-
将运行结果截图保存进文档中
-
将网络的准确率截图放入文档中
-
总结自己的炼丹过程,并记录下过程中的思考
Part 05 🤬知识蒸馏!!!
如果出题人在做自己出的题时没出错的话,那么现在各位应该和出题人一样对Part 04中自己搭建的模型充满了大大的问号🧐
“为什么我的准确率这么低啊啊啊?!!😡”
事实上,大家很有可能遇到了老生常谈的欠拟合问题。也就是说我们自行搭建的模型参数量太少,难以学习到数据集中53种类别的Card的详细信息,导致分类不准确。
📚 知识点
接下来,让我们进入正题:
-
知识蒸馏:请大家自行学习知识蒸馏这一概念。
-
学生模型与教师模型:解释什么是学生模型,什么是教师模型?
-
温度系数:解释什么是温度系数?
-
知识蒸馏流程:用自己的话解释知识蒸馏的整个流程。
👩🏫 实践一下叭
以下是知识蒸馏的核心训练部分框架代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
from models import alexnet, MicroCNN # 假设你已经在models.py中定义了这两个模型
import os
import logging
from tqdm import tqdm
# 配置日志
logging.basicConfig(filename='distill.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 定义图像转换
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(), # 将图像转换为Tensor
'''
数据增强操作···
'''
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])
transform_test = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(), # 将图像转换为Tensor
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化
])
dataset = datasets.ImageFolder(root='./train', transform=transform)
train_loader = DataLoader(dataset, batch_size=128, shuffle=True)
testset = datasets.ImageFolder(root='./test', transform=transform_test)
test_loader = DataLoader(testset, batch_size=128, shuffle=False)
# 初始化模型
teacher_model = alexnet()
student_model = MicroCNN()
teacher_model.to('cuda')
# net.to('cuda')
PATH = 'tiny_cnn.pt'
if os.path.exists(PATH):
logging.info("加载模型...")
model = torch.load(PATH, map_location='cuda')
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in model.state_dict() .items():
name = k[7:] # remove `module.`
new_state_dict[name] = v
# load params
teacher_model.load_state_dict(new_state_dict)
# net.load_state_dict(model.state_dict())
teacher_model.to('cuda')
# 使用 nn.DataParallel 包装模型以在多个 GPU 上训练
if torch.cuda.device_count() > 1:
logging.info(f"使用 {torch.cuda.device_count()} 个 GPU 进行训练")
teacher_model = nn.DataParallel(teacher_model)
else:
logging.info("模型文件不存在,从头开始训练。")
student_model.to('cuda')
# 使用 nn.DataParallel 包装模型以在多个 GPU 上训练
if torch.cuda.device_count() > 1:
logging.info(f"使用 {torch.cuda.device_count()} 个 GPU 进行训练")
teacher_model = nn.DataParallel(teacher_model)
student_model.to('cuda')
# net.to('cuda')
PATH = 'micro_cnn.pt'
if os.path.exists(PATH):
logging.info("加载模型...")
model = torch.load(PATH, map_location='cuda')
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in model.items():
name = k[7:] # remove `module.`
new_state_dict[name] = v
# load params
student_model.load_state_dict(new_state_dict)
# net.load_state_dict(model.state_dict())
student_model.to('cuda')
# 使用 nn.DataParallel 包装模型以在多个 GPU 上训练
if torch.cuda.device_count() > 1:
logging.info(f"使用 {torch.cuda.device_count()} 个 GPU 进行训练")
student_model = nn.DataParallel(student_model)
else:
logging.info("模型文件不存在,从头开始训练。")
student_model.to('cuda')
# 使用 nn.DataParallel 包装模型以在多个 GPU 上训练
if torch.cuda.device_count() > 1:
logging.info(f"使用 {torch.cuda.device_count()} 个 GPU 进行训练")
student_model = nn.DataParallel(student_model)
# 冻结教师模型的参数
for param in teacher_model.parameters():
param.requires_grad = False
teacher_model.to('cuda')
student_model.to('cuda')
# 蒸馏温度参数
temperature = 3.0
# 损失函数
def distillation_loss(output_student, output_teacher, temperature):
# soft_output_student = nn.functional.softmax(output_student / temperature, dim=1)
soft_output_student = nn.functional.softmax(output_student , dim=1)
soft_output_teacher = nn.functional.softmax(output_teacher / temperature, dim=1)
return nn.functional.kl_div(soft_output_student, soft_output_teacher, reduction='batchmean')
# 优化器
optimizer = optim.SGD(student_model.parameters(), lr=0.0005, momentum=0.9, weight_decay=0.005, nesterov=True)
# 训练过程
def train(model, loader, optimizer, device):
model.train()
total_loss = 0
train_total = 0
train_correct = 0
for i, (inputs, labels) in tqdm(enumerate(loader), total=len(loader), desc='Train'): # 正确调用tqdm
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs_teacher = teacher_model(inputs)
outputs_student = model(inputs)
_, predicted = torch.max(outputs_student.data, 1)
train_total += labels.size(0)
train_correct += (predicted == labels).sum().item() # 计算测试集准确率
loss = distillation_loss(outputs_student, outputs_teacher, temperature)
loss.backward()
optimizer.step()
total_loss += loss.item()
train_accuracy = 100 * train_correct / train_total
logging.info(f'[{epoch + 1}] Train Acc: {train_accuracy:.2f}%')
return total_loss / len(loader)
请各位同学根据核心框架,并借助上两个Part中的代码,搭建一个完整的知识蒸馏过程。
要求:
-
以Part 03中的模型为教师模型
-
以Part 04中自己构建的网络为学生模型
-
将教师模型的知识蒸馏到学生模型中
提交要求
-
请完成题目中所提到的知识点的学习笔记
-
将完整代码嵌入至文档中
-
详细介绍代码流程,并简述完成本题的过程
-
详细写出在进行知识蒸馏时,观察到的现象是什么?训练效果与预期的差异大不大?并且思考出现这些现象的原因是什么?
或许知识蒸馏后的结果也有些差强人意,接下来就需要开动你们聪明的🧠仔细思考思考🤔如何将我们在前面的4个Part中所学的知识充分利用起来,使得大家在Part 04中自行构建的模型的准确率尽可能高呢😵
-
请大家充分利用_在前面的4个Part中所学的知识_,对Part 04中大家自行构建的模型进行训练与调整,使其在测试集上的准确率尽可能高。
-
请将完成上一题的思考过程、解决方案、踩过的坑······如上的一系列流程记录成一份详细的学习笔记跟在文档的最后(想记什么记什么,爱记什么记什么,能记什么记什么···)。
-
将最终得到的学生模型用于测试集进行预测,并将运行结果截图放入文档中
Next Level!!!🤯🤯🤯
不用猜都知道知道大家很快就将前五个Part速通了,于是善解人意的出题人贴心地为大家设计了两个next levels✊✊✊
Ⅰ NEXT LEVEL
交互与部署(云文档链接,大家直接点击即可)
Ⅱ NEXT NEXT LEVEL
NLP&CV附加题--探索Transformer模型底层(云文档链接,大家直接点击即可)