Datawhale X 李宏毅苹果书AI夏令营深度学习进阶 task3笔记

1.批量归一化

        批量归一化(BN):一种用于改善神经网络训练过程的技术,通过规范化层的输入来加速训练并提高模型的稳定性。
        内部协变量偏移:在深度网络中,每一层的输入分布会随着网络训练而变化,BN 通过规范化来减少这种偏移。
        γ 和 β 参数:BN 引入了可学习的参数 γ(缩放因子)和 β(平移因子),以允许模型调整归一化后的特征。
        训练与测试:在训练时,BN 使用当前批量的统计数据来归一化;在测试时,使用训练过程中累积的移动平均统计数据。

        假设两个参数对损失的斜率差别非常大,在 w1 这个方向上面,斜率变化很小,在 w2 这个方向上面斜率变化很大。我们采取另一种方法,并非修改学习率,而是直接把这很陡峭的w2给改了,但是为什么w1和w2会产生如此差距呢?

图1.1 训练遇到情况图

        用以下一个简单的线性模型举例,w1的修改会影响到y,进而影响到L,那么w1改变对L影响很小时也就相当于上述w1的斜率较小。一个简单的可能是w1中的样本值都较小,w2中的样本值都较大,根据数学运算法则可以简单推导出两者对于L的改变呈不同权重。

        所以既然在这个线性的的模型里面,当输入的特征,每一个维度的值,它的范围 差距很大的时候,我们就可能产生像这样子的误差表面,就可能产生不同方向,斜率非常不同,坡度非常不同的误差表面所以怎么办呢,有没有可能给特征里面不同的维度,让它有同样的数值的范围。如果我们可以给不同的维度,同样的数值范围的话,那我们可能就可以制造比较好的误差表面,让训练变得比较容易一点其实有很多不同的方法,这些不同的方法往往就合起来统称为特征归一化(feature normalization)。

图1.2 简单线性模型图 

图1.3 归一化目标

        标准化(Z值归一化):将训练数据的特征向量集合起来,计算平均值和标准差后减掉这一个维度算出来的平均值,再除掉这个维度,算出来的标准差,得到新的数值放回。归一化完成后,该维度上的数值会平均为0,方差为1,所以该排数值会在0上下,将每个维度的数值都进行归一化后,就可以得到一个较好的误差表面,在训练时可以使梯度下降时损失收敛更快,训练更顺利。

图1.4 Z值归一化

        截至目前,我们成功将w1进行了归一化,但是我们发现还有w2,而对于w2,a和z也是一种特征,也要对其进行归一化,还有更多其他参数,当我们全部归一化后发现,数据量太大了该怎么办?数据过多,GPU的显存无法将所有整个数据集的数据都加载进去,那么我们将考虑只训练一个批量里面的样本,将批量假设 64,这个网络就是把 64 笔数据读进去,计算这 64 笔数据的 µ,σ,对这 64 笔数据做归一化。因为实际实现的时候,只对一个批量里面的数据做归一化,所以技巧称为批量归一 化。一定要有一个够大的批量,才算得出 µ,σ。所以批量归一化适用于批量大小比较大的时候,批量大小如果比较大,也许这个批量大小里面的数据就足以表示整个数据集的分布。这个时候就不需要对整个数据集做特征归一化,而改成只在一个批量上做特征归一化作为近似。

图1.5 批量归一化可以理解为网络的一部分

        至此,我们引入了批量归一化,这是因为在深度神经网络的训练过程中,每一层的输入数据分布会随着网络权重的更新而发生变化。这种数据分布的变化被称为内部协变量偏移。由于每一层的输入都依赖于前一层的输出,前层参数的更新会导致后层输入的分布发生变化,这使得网络训练变得困难。那么除此以外BN还有优势吗?

        由于规范化操作减少了梯度消失或爆炸的问题,可以使用更大的学习率,从而加快了训练速度。此外,批量归一化通过减少过拟合,提高了模型的泛化能力。它在一定程度上起到了正则化的作用。

2.卷积神经网络

        卷积神经网络(CNN):主要用于图像识别和分类任务。它通过模拟人类视觉系统的工作原理,利用卷积层来提取图像的特征。
        图像分类:CNN 通过学习图像中的特征来识别图像中的物体,如区分猫和狗。
        张量:图像在 CNN 中被表示为三维张量,其中包含宽度、高度和颜色通道。
        卷积操作:通过卷积层和滤波器(或称为卷积核)来提取图像的局部特征。
        感受野(Receptive Field):每个神经元只关注输入数据的一个局部区域,这个区域称为感受野。
        参数共享:在卷积层中,相同的滤波器在整个输入图像上滑动,共享权重,减少了模型的参数数量。
        步幅(Stride):滤波器在图像上移动的步长。
        填充(Padding):在图像边缘补充像素,以控制输出特征图的大小。
        特征映射(Feature Map):卷积层的输出,表示为多个通道的特征图。
        汇聚(Pooling):减少特征图的空间尺寸,降低参数数量和计算复杂度,同时保持特征的重要信息。
        多层卷积网络:通过堆叠多个卷积层和汇聚层,可以提取更复杂的特征。
        下采样:通过减少图像的分辨率来提取图像中的主要特征,同时减少计算量。
        全连接层:在卷积网络的最后,通常使用全连接层来进行分类。
        应用领域:CNN 不仅用于图像识别,还可以用于语音识别、自然语言处理等其他领域。

        卷积神经网络是一种深度学习模型,它特别适合处理具有网格结构的数据,比如图像。图像可以看作是一个由像素值组成的网格,每个像素点都有颜色强度值。

图2.1 把图像作为模型输入

        彩色通道指RGB三色,在我们处理图像时,往往要先进行尺寸大小调整至统一,但是根据三原色所定义的特征向量过长,导致权重数量巨大,过拟合风险太高了。该如何进行调整?

        在此便要提出CNN的优势了:利用图像特性,不需要全连接,减少参数数量。

        作为图像分类模型,分类结果表示为独热向量。 使用softmax函数处理模型输出。

图2.2 图像分类

        我们在辨认一张图片的时候,多数不需要仔细观察整张图片,神经网络识别也是同理,它所需要做的是检测图中的重要模式。此外,我们再引入一个概念,感受野,每个神经元只关心一个小范围。

图2.3 神经网络检测模式

        如图2.4所示,感受野只看了3x3x3=27个权重,再加上偏置就得到了输出,这相比全连接简直是种奇迹。

图2.4 感受野

        再引入一个新问题,两个感受野能否重叠呢?答案是可以。绿色的神经元的感受野跟蓝色的、黄色的神经元都有一些重叠的空间。我们没有办法检测所有的模式,所以同个范围可以有多个 不同的神经元,即多个神经元可以去守备同一个感受野。理论上感受野可以任意安排,在此给出一个最常见的安排方式。

图2.5 感受野重叠

        在图像识别里面,一般核大小不会设太大,3 × 3 的核大小就足够了,我们把左上角的感受野往右移一个步幅,就制造出一个新的守备范围,即新的感受野。 移动的量称为步幅(stride),步幅一般设为1或2即可,它是个超参数,可以人为控制。

图2.6 步幅

        又有个问题,超出去了咋办?即使步幅为2也可能遇到感受野“跑出去”的情况,这时我们就需要填充。一般我们采用零填充。

图2.7 填充

        又双叒叕有新问题,同样的模式可能出现在图像的不同区域,又咋办?比如说一张图片,需要检测的模式为鸟嘴,但是图中有好几只鸟。我们可以采用参数共享,减少模型复杂度。

图2.8 参数共享

         全连接网络具有最大的灵活性,但参数数量庞大,容易导致过拟合。卷积神经网络通过引入感受野(局部连接)和参数共享减少了参数数量,降低了模型的灵活性,但也减少了过拟合的风险。

图2.9 感受野与全连接层

        多个神经元共享相同的权重,称为滤波器(filter),我们发现即使对图像进行下采样(如减少图像的尺寸),图像中的重要模式仍然可以被检测出来。于是我们引入汇聚

        汇聚操作不包含参数,通常用于降低特征维度,减少计算量。常见的汇聚操作包括最大汇聚(Max Pooling)和平均汇聚(Mean Pooling)。

图2.10 最大汇聚

图2.11 最大汇聚结果

3.实践部分

        实践部分只需要根据步骤一步一步完成即可。

1. 导入库/工具包

        导入内容基本与task2没有区别,感觉在进行相关大模型实践时,可以以此为模板进行修改或参考。

# 导入必要的库
import numpy as np
import pandas as pd
import torch
import os
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
# “ConcatDataset” 和 “Subset” 在进行半监督学习时可能是有用的。
from torch.utils.data import ConcatDataset, DataLoader, Subset, Dataset
from torchvision.datasets import DatasetFolder, VisionDataset
# 这个是用来显示进度条的。
from tqdm.auto import tqdm
import random
# 设置随机种子以确保实验结果的可重复性
myseed = 6666

# 确保在使用CUDA时,卷积运算具有确定性,以增强实验结果的可重复性
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

# 为numpy和pytorch设置随机种子
np.random.seed(myseed)
torch.manual_seed(myseed)

# 如果使用CUDA,为所有GPU设置随机种子
if torch.cuda.is_available():
    torch.cuda.manual_seed_all(myseed)

2. 数据准备与预处理

# 在测试和验证阶段,通常不需要图像增强。
# 我们所需要的只是调整PIL图像的大小并将其转换为Tensor。
test_tfm = transforms.Compose([
    transforms.Resize((128, 128)),
    transforms.ToTensor(),
])

# 不过,在测试阶段使用图像增强也是有可能的。
# 你可以使用train_tfm生成多种图像,然后使用集成方法进行测试。
train_tfm = transforms.Compose([
    # 将图像调整为固定大小(高度和宽度均为128)
    transforms.Resize((128, 128)),
    # TODO:你可以在这里添加一些图像增强的操作。

    # ToTensor()应该是所有变换中的最后一个。
    transforms.ToTensor(),
])
class FoodDataset(Dataset):
    """
    用于加载食品图像数据集的类。

    该类继承自Dataset,提供了对食品图像数据集的加载和预处理功能。
    它可以自动从指定路径加载所有的jpg图像,并对这些图像应用给定的变换。
    """

    def __init__(self, path, tfm=test_tfm, files=None):
        """
        初始化FoodDataset实例。

        参数:
        - path: 图像数据所在的目录路径。
        - tfm: 应用于图像的变换方法(默认为测试变换)。
        - files: 可选参数,用于直接指定图像文件的路径列表(默认为None)。
        """
        super(FoodDataset).__init__()
        self.path = path
        # 列出目录下所有jpg文件,并按顺序排序
        self.files = sorted([os.path.join(path, x) for x in os.listdir(path) if x.endswith(".jpg")])
        if files is not None:
            self.files = files  # 如果提供了文件列表,则使用该列表
        self.transform = tfm  # 图像变换方法

    def __len__(self):
        """
        返回数据集中图像的数量。

        返回:
        - 数据集中的图像数量。
        """
        return len(self.files)

    def __getitem__(self, idx):
        """
        获取给定索引的图像及其标签。

        参数:
        - idx: 图像在数据集中的索引。

        返回:
        - im: 应用了变换后的图像。
        - label: 图像对应的标签(如果可用)。
        """
        fname = self.files[idx]
        im = Image.open(fname)
        im = self.transform(im)  # 应用图像变换

        # 尝试从文件名中提取标签
        try:
            label = int(fname.split("/")[-1].split("_")[0])
        except:
            label = -1  # 如果无法提取标签,则设置为-1(测试数据无标签)

        return im, label
# 构建训练和验证数据集
# "loader" 参数定义了torchvision如何读取数据
train_set = FoodDataset("./hw3_data/train", tfm=train_tfm)
# 创建训练数据加载器,设置批量大小、是否打乱数据顺序、是否使用多线程加载以及是否固定内存地址
train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=0, pin_memory=True)
# 构建验证数据集
# "loader" 参数定义了torchvision如何读取数据
valid_set = FoodDataset("./hw3_data/valid", tfm=test_tfm)
# 创建验证数据加载器,设置批量大小、是否打乱数据顺序、是否使用多线程加载以及是否固定内存地址
valid_loader = DataLoader(valid_set, batch_size=batch_size, shuffle=True, num_workers=0, pin_memory=True)

3. 定义模型

        这段代码定义了一个图像分类器类(Classifier),继承自PyTorch的nn.Module。该分类器通过一系列卷积层、批归一化层、激活函数和池化层构建卷积神经网络(CNN),用于提取图像特征。随后,这些特征被输入到全连接层进行分类,最终输出11个类别的概率,用于图像分类任务。

class Classifier(nn.Module):
    """
    定义一个图像分类器类,继承自PyTorch的nn.Module。
    该分类器包含卷积层和全连接层,用于对图像进行分类。
    """
    def __init__(self):
        """
        初始化函数,构建卷积神经网络的结构。
        包含一系列的卷积层、批归一化层、激活函数和池化层。
        """
        super(Classifier, self).__init__()
        # 定义卷积神经网络的序列结构
        self.cnn = nn.Sequential(
            nn.Conv2d(3, 64, 3, 1, 1),  # 输入通道3,输出通道64,卷积核大小3,步长1,填充1
            nn.BatchNorm2d(64),        # 批归一化,作用于64个通道
            nn.ReLU(),                 # ReLU激活函数
            nn.MaxPool2d(2, 2, 0),      # 最大池化,池化窗口大小2,步长2,填充0
            
            nn.Conv2d(64, 128, 3, 1, 1), # 输入通道64,输出通道128,卷积核大小3,步长1,填充1
            nn.BatchNorm2d(128),        # 批归一化,作用于128个通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),      # 最大池化,池化窗口大小2,步长2,填充0
            
            nn.Conv2d(128, 256, 3, 1, 1), # 输入通道128,输出通道256,卷积核大小3,步长1,填充1
            nn.BatchNorm2d(256),        # 批归一化,作用于256个通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),      # 最大池化,池化窗口大小2,步长2,填充0
            
            nn.Conv2d(256, 512, 3, 1, 1), # 输入通道256,输出通道512,卷积核大小3,步长1,填充1
            nn.BatchNorm2d(512),        # 批归一化,作用于512个通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),       # 最大池化,池化窗口大小2,步长2,填充0
            
            nn.Conv2d(512, 512, 3, 1, 1), # 输入通道512,输出通道512,卷积核大小3,步长1,填充1
            nn.BatchNorm2d(512),        # 批归一化,作用于512个通道
            nn.ReLU(),
            nn.MaxPool2d(2, 2, 0),       # 最大池化,池化窗口大小2,步长2,填充0
        )
        # 定义全连接神经网络的序列结构
        self.fc = nn.Sequential(
            nn.Linear(512*4*4, 1024),    # 输入大小512*4*4,输出大小1024
            nn.ReLU(),
            nn.Linear(1024, 512),        # 输入大小1024,输出大小512
            nn.ReLU(),
            nn.Linear(512, 11)           # 输入大小512,输出大小11,最终输出11个类别的概率
        )

    def forward(self, x):
        """
        前向传播函数,对输入进行处理。
        
        参数:
        x -- 输入的图像数据,形状为(batch_size, 3, 128, 128)
        
        返回:
        输出的分类结果,形状为(batch_size, 11)
        """
        out = self.cnn(x)               # 通过卷积神经网络处理输入
        out = out.view(out.size()[0], -1)  # 展平输出,以适配全连接层的输入要求
        return self.fc(out)             # 通过全连接神经网络得到最终输出

4. 定义损失函数和优化器等其他配置

        这段代码实现了图像分类模型的初始化和训练配置,目的是准备好训练环境和参数。它选择合适的设备(GPU或CPU),设置模型、批量大小、训练轮数、提前停止策略,定义了损失函数和优化器,为后续的模型训练奠定了基础。

# 根据GPU是否可用选择设备类型
device = "cuda" if torch.cuda.is_available() else "cpu"

# 初始化模型,并将其放置在指定的设备上
model = Classifier().to(device)

# 定义批量大小
batch_size = 64

# 定义训练轮数
n_epochs = 8

# 如果在'patience'轮中没有改进,则提前停止
patience = 5

# 对于分类任务,我们使用交叉熵作为性能衡量标准
criterion = nn.CrossEntropyLoss()

# 初始化优化器,您可以自行调整一些超参数,如学习率
optimizer = torch.optim.Adam(model.parameters(), lr=0.0003, weight_decay=1e-5)

5. 训练模型

        这段代码实现了一个图像分类模型的训练和验证循环,目的是通过多轮训练(epochs)逐步优化模型的参数,以提高其在验证集上的性能,并保存效果最好的模型。训练阶段通过前向传播、计算损失、反向传播和参数更新来优化模型,验证阶段评估模型在未见过的数据上的表现。如果验证集的准确率超过了之前的最好成绩,保存当前模型,并在连续多轮验证性能未提升时提前停止训练。

# 初始化追踪器,这些不是参数,不应该被更改
stale = 0
best_acc = 0

for epoch in range(n_epochs):
    # ---------- 训练阶段 ----------
    # 确保模型处于训练模式
    model.train()

    # 这些用于记录训练过程中的信息
    train_loss = []
    train_accs = []

    for batch in tqdm(train_loader):
        # 每个批次包含图像数据及其对应的标签
        imgs, labels = batch
        # imgs = imgs.half()
        # print(imgs.shape,labels.shape)

        # 前向传播数据。(确保数据和模型位于同一设备上)
        logits = model(imgs.to(device))

        # 计算交叉熵损失。
        # 在计算交叉熵之前不需要应用softmax,因为它会自动完成。
        loss = criterion(logits, labels.to(device))

        # 清除上一步中参数中存储的梯度
        optimizer.zero_grad()

        # 计算参数的梯度
        loss.backward()

        # 为了稳定训练,限制梯度范数
        grad_norm = nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)

        # 使用计算出的梯度更新参数
        optimizer.step()

        # 计算当前批次的准确率
        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()

        # 记录损失和准确率
        train_loss.append(loss.item())
        train_accs.append(acc)

    train_loss = sum(train_loss) / len(train_loss)
    train_acc = sum(train_accs) / len(train_accs)

    # 打印信息
    print(f"[ 训练 | {epoch + 1:03d}/{n_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}")

6. 评估模型

        训练完成后,需要在测试集上评估模型的性能。通过计算准确率来衡量模型在测试集上的表现。

# ---------- 验证阶段 ----------
    # 确保模型处于评估模式,以便某些模块如dropout被禁用,模型能够正常工作
    model.eval()

    # 这些用于记录验证过程中的信息
    valid_loss = []
    valid_accs = []

    # 按批次迭代验证集
    for batch in tqdm(valid_loader):
        # 每个批次包含图像数据及其对应的标签
        imgs, labels = batch
        # imgs = imgs.half()

        # 我们在验证阶段不需要梯度。
        # 使用 torch.no_grad() 加速前向传播过程。
        with torch.no_grad():
            logits = model(imgs.to(device))

        # 我们仍然可以计算损失(但不计算梯度)。
        loss = criterion(logits, labels.to(device))

        # 计算当前批次的准确率
        acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()

        # 记录损失和准确率
        valid_loss.append(loss.item())
        valid_accs.append(acc)
        # break

    # 整个验证集的平均损失和准确率是所记录值的平均
    valid_loss = sum(valid_loss) / len(valid_loss)
    valid_acc = sum(valid_accs) / len(valid_accs)

    # 打印信息
    print(f"[ 验证 | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")

    # 更新日志
    if valid_acc > best_acc:
        with open(f"./{_exp_name}_log.txt", "a"):
            print(f"[ 验证 | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f} -> 最佳")
    else:
        with open(f"./{_exp_name}_log.txt", "a"):
            print(f"[ 验证 | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")
# 李老师的课程原文件里面确实缺少write,如果想要log文件里面有内容,可以按照下面的参考,此部分不是重点
#if valid_acc > best_acc:
#    with open(f"./{_exp_name}_log.txt", "a") as log_file:
#        log_file.write(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f} -> best\n")
#    print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f} -> best")
#else:
#    with open(f"./{_exp_name}_log.txt", "a") as log_file:
#        log_file.write(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}\n")
#    print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")




    # 保存模型
    if valid_acc > best_acc:
        print(f"在第 {epoch} 轮找到最佳模型,正在保存模型")
        torch.save(model.state_dict(), f"{_exp_name}_best.ckpt")  # 只保存最佳模型以防止输出内存超出错误
        best_acc = valid_acc
        stale = 0
    else:
        stale += 1
        if stale > patience:
            print(f"连续 {patience} 轮没有改进,提前停止")
            break

7. 进行预测

        最后的代码构建一个测试数据集和数据加载器,以便高效地读取数据。实例化并加载预训练的分类器模型,并将其设置为评估模式。在不计算梯度的情况下,遍历测试数据,使用模型进行预测,并将预测标签存储在列表中。将预测结果与测试集的ID生成一个DataFrame,并将其保存为submission.csv文件。

# 构建测试数据集
# "loader"参数指定了torchvision如何读取数据
test_set = FoodDataset("./hw3_data/test", tfm=test_tfm)
# 创建测试数据加载器,批量大小为batch_size,不打乱数据顺序,不使用多线程,启用pin_memory以提高数据加载效率
test_loader = DataLoader(test_set, batch_size=batch_size, shuffle=False, num_workers=0, pin_memory=True)

# 实例化分类器模型,并将其转移到指定的设备上
model_best = Classifier().to(device)

# 加载模型的最优状态字典
model_best.load_state_dict(torch.load(f"{_exp_name}_best.ckpt"))

# 将模型设置为评估模式
model_best.eval()

# 初始化一个空列表,用于存储所有预测标签
prediction = []

# 使用torch.no_grad()上下文管理器,禁用梯度计算
with torch.no_grad():
    # 遍历测试数据加载器
    for data, _ in tqdm(test_loader):
        # 将数据转移到指定设备上,并获得模型的预测结果
        test_pred = model_best(data.to(device))
        # 选择具有最高分数的类别作为预测标签
        test_label = np.argmax(test_pred.cpu().data.numpy(), axis=1)
        # 将预测标签添加到结果列表中
        prediction += test_label.squeeze().tolist()

# 创建测试csv文件
def pad4(i):
    """
    将输入数字i转换为长度为4的字符串,如果长度不足4,则在前面补0。
    :param i: 需要转换的数字
    :return: 补0后的字符串
    """
    return "0" * (4 - len(str(i))) + str(i)

# 创建一个空的DataFrame对象
df = pd.DataFrame()
# 使用列表推导式生成Id列,列表长度等于测试集的长度
df["Id"] = [pad4(i) for i in range(len(test_set))]
# 将预测结果赋值给Category列
df["Category"] = prediction
# 将DataFrame对象保存为submission.csv文件,不保存索引
df.to_csv("submission.csv", index=False)        

8.完成结果附图

图3.8.1 All Classes t-SNE Visualization

图3.8.2 Class 5 t-SNE Visualization

4.学习交流会

        9月2日Datawhale进行了《深度学习详解》 进阶班-学习交流会,首先会议讲解了使用AI进行辅助学习的几个小技巧,可以先提出总体上对概念的理解,再对准某个点进行追问,同时可以让大模型举几个详细例子辅助理解,最后可以进行反问,如“我觉得你说的不对”等等,当模型多次给出的答案基本一致后,基本就可以得到一个较为准确的结果了。如果是数学问题,还可以让大模型一步步给出步骤,类似在提问Radam算法时,对于大模型给出的概率论问题不理解,可以要求它再详细解释一下概率论中的内容。

图.1 学习交流会图

        此外,会议中不少同学也进行了自我介绍与故事分享,通过Datawhale学习到很多内容,也认识了不少很厉害的大佬和老师,收获颇丰~

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值