当前位置: 首页 > news >正文

基于CNN卷积神经网络迁移学习的图像识别实现

基于CNN卷积神经网络迁移学习的图像识别实现

  • 基于CNN卷积神经网络迁移学习的图像识别实现
    • 写在前面
    • 一,原理介绍
      • 迁移学习的基本方法
        • 1.样本迁移(Instance based TL)
        • 2.特征迁移(Feature based TL)
        • 3.模型迁移(Parameter based TL)
      • 4.关系迁移(Relation based TL)
    • 二. 准备工作
      • 1.依赖库安装
      • 2.IDE设置
      • 3.检查GPU-cuda核心是否可用
    • 三. 具体实现
      • 1.导入所需软件包
      • 2.加载数据
      • 3.可视化数据
      • 4.训练模型
      • 5.可视化模型预测
      • 6.卷积神经网络微调
      • 7.评估和训练
      • 8.神经网络固定特征提取
      • 9.自定义测试集测试
      • 10.结论
        • 1. 微调(Fine-tuning)
        • 2. 使用固定特征提取器(Fixed Feature Extractor)
      • 总结
    • 四.全部代码
    • 写在最后

基于CNN卷积神经网络迁移学习的图像识别实现

写在前面

笔者是一名ADAS底层软件工程师。在繁忙的嵌入式软件开发工作之余,我对新技术的保持浓厚兴趣。近年来,深度学习特别是卷积神经网络(CNN)的迅猛发展。尽管我的主要工作集中在车载系统和嵌入式应用,但我深知新技术对未来的巨大潜力。因此,我自学CNN及其在迁移学习中的应用,并希望将自己的学习经验整理成文。这篇博客不仅是我个人学习的总结,也是希望为那些对CNN迁移学习感兴趣的同学提供实用的参考。通过这篇笔记,我将分享一些关键概念和部署经验,包括如何进行模型微调、固定特征提取器的使用方法,以及如何利用训练好的模型进行实际预测。我真诚希望这篇博客能对大家有所帮助,欢迎大家在评论区留言交流,共同探讨和学习!

一,原理介绍

们常常将迁移学习和神经网络的训练上存在误区将其混为一谈。实际上,这两个概念最初是独立的。迁移学习是机器学习的一个分支,其中有许多方法并不依赖于神经网络。然而,随着神经网络的快速发展、强大能力和广泛应用,迁移学习的研究逐渐与神经网络紧密联系起来。

迁移学习(transfer learning)通俗来讲,就是运用已有的知识来学习新的知识,核心是找到已有知识和新知识之间的相似性,用成语来说就是举一反三。由于直接对目标域从头开始学习成本太高,我们故而转向运用已有的相关知识来辅助尽快地学习新知识。比如,已经会下中国象棋,就可以类比着来学习国际象棋:已经会编写Java程序,就可以类比着来学习C#;已经学会英语,就可以类比着来学习法语;等等。世间万事万物皆有共性,如何合理地找寻它们之间的相似性,进而利用这个桥梁来帮助学习新知识,是迁移学习的核心问题。

迁移学习的基本方法

1.样本迁移(Instance based TL)

在源域中找到与目标域相似的数据,把这个数据的权值进行调整,使得新的数据与目标域的数据进行匹配。下图的例子就是找到源域的例子3,然后加重该样本的权值,使得在预测目标域时的比重加大。优点是方法简单,实现容易。缺点在于权重的选择与相似度的度量依赖经验,且源域与目标域的数据分布往往不同。
在这里插入图片描述

2.特征迁移(Feature based TL)

假设源域和目标域含有一些共同的交叉特征,通过特征变换,将源域和目标域的特征变换到相同空间,使得该空间中源域数据与目标域数据具有相同分布的数据分布,然后进行传统的机器学习。优点是对大多数方法适用,效果较好。缺点在于难于求解,容易发生过适配。

在这里插入图片描述

3.模型迁移(Parameter based TL)

假设源域和目标域共享模型参数,是指将之前在源域中通过大量数据训练好的模型应用到目标域上进行预测,比如利用上千万的图象来训练好一个图象识别的系统,当我们遇到一个新的图象领域问题的时候,就不用再去找几千万个图象来训练了,只需把原来训练好的模型迁移到新的领域,在新的领域往往只需几万张图片就够,同样可以得到很高的精度。优点是可以充分利用模型之间存在的相似性。缺点在于模型参数不易收敛。

在这里插入图片描述

4.关系迁移(Relation based TL)

假设两个域是相似的,那么它们之间会共享某种相似关系,将源域中逻辑网络关系应用到目标域上来进行迁移,比方说生物病毒传播到计算机病毒传播的迁移。

在这里插入图片描述

对于CNN的迁移学习网上有很多大神的讲解都非常精彩,笔者只是简单介绍基本的概念,只要呢让大家明白为什么要进行迁移学习足以,我们还是着手实践,格物致知。

文章推荐:
链接: 微软亚洲研究院对迁移学习问题的回答

二. 准备工作

关于开发环境笔者是用Anaconda+PyCharm,个人认为这样包管理和开发都比较方便,当然因人而异,适合自己就好

1.依赖库安装

我将CondaList打出来,各位对照着版本安装就可以conda list

# Name                    Version                   Build  Channel
ca-certificates           2024.7.2             haa95532_0    defaults
contourpy                 1.1.1                    pypi_0    pypi
cycler                    0.12.1                   pypi_0    pypi
filelock                  3.15.4                   pypi_0    pypi
fonttools                 4.53.1                   pypi_0    pypi
fsspec                    2024.6.1                 pypi_0    pypi
importlib-resources       6.4.4                    pypi_0    pypi
kiwisolver                1.4.5                    pypi_0    pypi
libffi                    3.4.4                hd77b12b_1    defaults
matplotlib                3.7.5                    pypi_0    pypi
mpmath                    1.3.0                    pypi_0    pypi
networkx                  3.1                      pypi_0    pypi
numpy                     1.24.4                   pypi_0    pypi
openssl                   3.0.14               h827c3e9_0    defaults
pillow                    10.4.0                   pypi_0    pypi
pip                       24.2             py38haa95532_0    defaults
pyparsing                 3.1.4                    pypi_0    pypi
python                    3.8.19               h1aa4202_0    defaults
python-dateutil           2.9.0.post0              pypi_0    pypi
setuptools                72.1.0           py38haa95532_0    defaults
six                       1.16.0                   pypi_0    pypi
sqlite                    3.45.3               h2bbff1b_0    defaults
sympy                     1.13.2                   pypi_0    pypi
torchvision               0.19.0                   pypi_0    pypi
typing-extensions         4.12.2                   pypi_0    pypi
vc                        14.40                h2eaa2aa_0    defaults
vs2015_runtime            14.40.33807          h98bb1dd_0    defaults
wheel                     0.43.0           py38haa95532_0    defaults

2.IDE设置

其实这就是Anaconda+PyCharm开发的方便之处了,直接新建项目并选择刚刚创建的conda环境就可以愉快的编写代码了。
在这里插入图片描述

下面是文件结构,所有的代码都写在main.py里了,所以创建python工程时直接生成一个mian文件就可以
这里是数据集下载链接🔗:数据集下载
模型文件夹需要手动创建一下,以保存原始,微调与固定特征提取器的模型。

    D:\PYTHON_CODE_WORKSPACE\BEESORANTS_DL-------------------主文件名│  main.py-----------------------------------------------全部代码│  readme.md├─.idea├─hymenoptera_data---------------------------------------数据集│  ├─train-----------------------------------------------训练数据集│  │  ├─ants│  │  │      0013035.jpg│  │  │      ...│  │  │      VietnameseAntMimicSpider.jpg│  │  ││  │  └─bees│  │          1092977343_cb42b38d62.jpg│  │          ...│  │          969455125_58c797ef17.jpg│  │          98391118_bdb1e80cce.jpg│  ││  └─val------------------------------------------------测试数据集│      ├─ants│      │      10308379_1b6c72e180.jpg│      │      ...│      │      Hormiga.jpg│      ││      └─bees│              1032546534_06907fe3b3.jpg│              ...│              936182217_c4caa5222d.jpg│              abeja.jpg│├─model-------------------------------------------------模型│      best_model_params.pt│      finetuned_model_params.pt│      initial_model_params.pt│├─redme_img│└─test_img----------------------------------------------自定义测试集220px-Acrobat.ant1web.jpg40708249_1415445497609.jpg

3.检查GPU-cuda核心是否可用

在开始编译模型开始预测之前,先看一下cuda核心版本和是否可用

输入nvidia-smi我的CUDA核心版本为12.5,从网上找到对应的pyrorch版本下载即可(但其实我更推荐conda下载)

Tue Sep  3 20:21:43 2024
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 555.99                 Driver Version: 555.99         CUDA Version: 12.5     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                  Driver-Model | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 3060 ...  WDDM  |   00000000:01:00.0  On |                  N/A |
| N/A   49C    P8             14W /  130W |     252MiB /   6144MiB |      3%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A      1060    C+G   ...1.28.6010\updated_web\WXWorkWeb.exe      N/A      |
|    0   N/A  N/A      6532    C+G   ...5n1h2txyewy\ShellExperienceHost.exe      N/A      |
+-----------------------------------------------------------------------------------------+

打开Anaconda.Nvigator,选择open with python当然你也可以在环境文件夹打开python解释器,依次输入下面指令:

import torch导入torch包

print(torch.__version__)打印torch版本验证是否安装成功

torch.cuda.is_available()验证GPU是否可用,虽然CPU也完全可以完成模型的计算(15-20m),但是GPU则更快(8m)

在这里插入图片描述

如果是FALSE的话那大概率是版本不符,如果你遇到了这种情况请从网上搜索一些相关教程,还是很多的,我比较幸运版本都完全匹配,和我一样的配置可以直接照抄我的conda list。

三. 具体实现

下面我会分块讲解每块代码是做什么的,以便你能完全理解代码,全部代码最后附上

1.导入所需软件包

# License: BSD
# Author: Sasank Chilamkurthyimport torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import torch.backends.cudnn as cudnn
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
from PIL import Image
from tempfile import TemporaryDirectorycudnn.benchmark = True
plt.ion()   # interactive mode

2.加载数据

我们将使用 torchvision 和 torch.utils.data 包来加载 数据。

我们今天要解决的问题是训练一个模型来对蚂蚁和蜜蜂进行分类。我们大约有 120 张蚂蚁和蜜蜂的训练图像。 每个类有 75 个验证图像。通常,这是如果从头开始训练,则要推广的小型数据集。由于我们 在使用迁移学习,我们应该能够合理地进行概括

此数据集是 imagenet 的一个非常小的子集

# Data augmentation and normalization for training
# Just normalization for validation
data_transforms = {'train': transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),'val': transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
}data_dir = 'data/hymenoptera_data'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),data_transforms[x])for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,shuffle=True, num_workers=4)for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classesdevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

3.可视化数据

def imshow(inp, title=None):"""Display image for Tensor."""inp = inp.numpy().transpose((1, 2, 0))mean = np.array([0.485, 0.456, 0.406])std = np.array([0.229, 0.224, 0.225])inp = std * inp + meaninp = np.clip(inp, 0, 1)plt.imshow(inp)if title is not None:plt.title(title)plt.pause(0.001)  # pause a bit so that plots are updated# Get a batch of training data
inputs, classes = next(iter(dataloaders['train']))# Make a grid from batch
out = torchvision.utils.make_grid(inputs)imshow(out, title=[class_names[x] for x in classes])

运行代码后输出四张图片和标签
在这里插入图片描述

4.训练模型

这段代码定义用于训练模型的函数 train_model
使用通用模型ResNet-18,它首先记录训练开始的时间,然后在每个训练周期(epoch)中,通过迭代训练集和验证集的数据来调整模型的参数。训练过程中,模型在训练阶段被设置为训练模式,在验证阶段被设置为评估模式。每次经过验证阶段后,如果模型在验证集上的准确率比之前最好的一次更好,它会保存当前的模型参数。训练完成后,函数将加载并返回在验证集上表现最好的模型权重。

    通用模型 ResNet-18 通常是为了在图像分类任务中利用其强大的特征提取能力。ResNet-18 是一个深度残差网络(Residual Network)
包含 18 层卷积和全连接层。它在 ImageNet 数据集上进行了预训练,因此能够识别和提取图像中的复杂特征。这些预训练的特征可以用于各种
计算机视觉任务,如分类、检测等。下面是如何使用 ResNet-18 的步骤:1-加载预训练模型:我们可以使用 PyTorch 提供的 torchvision.models 模块来加载一个预训练的 ResNet-18 模型,该模型已经在ImageNet 数据集上训练好了。2-冻结早期层:根据任务的需要,我们可以选择冻结模型的早期层,只训练最后一层。这意味着前几层的权重保持不变,我们只调整最后一层的权重。3-修改输出层:ResNet-18 的原始输出层用于 1000 类分类,但我们可能只需要区分更少的类别(例如蜜蜂和蚂蚁)。因此,我们需要替换掉模型的最后一层,使其输出我们需要的类别数量。4-训练模型:在我们特定的数据集上(例如蜜蜂和蚂蚁的图像数据集)进行训练,通过多轮次的训练调整最后一层的权重,使得模型能够准确地分类新图像。5-评估模型:在验证集上评估模型的性能,如果表现良好,我们可以保存最优的模型权重。    

def train_model(model, criterion, optimizer, scheduler, num_epochs=25):since = time.time()# Create a temporary directory to save training checkpointswith TemporaryDirectory() as tempdir:best_model_params_path = os.path.join(tempdir, 'best_model_params.pt')torch.save(model.state_dict(), best_model_params_path)best_acc = 0.0for epoch in range(num_epochs):print(f'Epoch {epoch}/{num_epochs - 1}')print('-' * 10)# Each epoch has a training and validation phasefor phase in ['train', 'val']:if phase == 'train':model.train()  # Set model to training modeelse:model.eval()   # Set model to evaluate moderunning_loss = 0.0running_corrects = 0# Iterate over data.for inputs, labels in dataloaders[phase]:inputs = inputs.to(device)labels = labels.to(device)# zero the parameter gradientsoptimizer.zero_grad()# forward# track history if only in trainwith torch.set_grad_enabled(phase == 'train'):outputs = model(inputs)_, preds = torch.max(outputs, 1)loss = criterion(outputs, labels)# backward + optimize only if in training phaseif phase == 'train':loss.backward()optimizer.step()# statisticsrunning_loss += loss.item() * inputs.size(0)running_corrects += torch.sum(preds == labels.data)if phase == 'train':scheduler.step()epoch_loss = running_loss / dataset_sizes[phase]epoch_acc = running_corrects.double() / dataset_sizes[phase]print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')# deep copy the modelif phase == 'val' and epoch_acc > best_acc:best_acc = epoch_acctorch.save(model.state_dict(), best_model_params_path)print()time_elapsed = time.time() - sinceprint(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')print(f'Best val Acc: {best_acc:4f}')# load best model weightsmodel.load_state_dict(torch.load(best_model_params_path, weights_only=True))return model

使用通用模型 ResNet-18 通常是为了在图像分类任务中利用其强大的特征提取能力。ResNet-18 是一个深度残差网络(Residual Network),包含 18 层卷积和全连接层。它在 ImageNet 数据集上进行了预训练,因此能够识别和提取图像中的复杂特征。这些预训练的特征可以用于各种计算机视觉任务,如分类、检测等。

下面是如何使用 ResNet-18 的步骤:

  1. 加载预训练模型:我们可以使用 PyTorch 提供的 torchvision.models 模块来加载一个预训练的 ResNet-18 模型,该模型已经在 ImageNet 数据集上训练好了。

  2. 冻结早期层:根据任务的需要,我们可以选择冻结模型的早期层,只训练最后一层。这意味着前几层的权重保持不变,我们只调整最后一层的权重。

  3. 修改输出层:ResNet-18 的原始输出层用于 1000 类分类,但我们可能只需要区分更少的类别(例如蜜蜂和蚂蚁)。因此,我们需要替换掉模型的最后一层,使其输出我们需要的类别数量。

  4. 训练模型:在我们特定的数据集上(例如蜜蜂和蚂蚁的图像数据集)进行训练,通过多轮次的训练调整最后一层的权重,使得模型能够准确地分类新图像。

  5. 评估模型:在验证集上评估模型的性能,如果表现良好,我们可以保存最优的模型权重。

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, models, transforms# 设置数据转换
data_transforms = {'train': transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),'val': transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
}# 数据集路径
data_dir = 'hymenoptera_data'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x])for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,shuffle=True, num_workers=4)for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classesdevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# 加载 ResNet-18 模型,使用预训练权重
model_ft = models.resnet18(weights='IMAGENET1K_V1')
num_ftrs = model_ft.fc.in_features# 修改最后一层以匹配我们的分类任务(蜜蜂和蚂蚁,2 类)
model_ft.fc = nn.Linear(num_ftrs, 2)model_ft = model_ft.to(device)criterion = nn.CrossEntropyLoss()# 优化器设置
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)# 学习率调度器
exp_lr_scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)# 训练模型
model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=25)# 训练完成后可以使用训练好的模型进行预测或评估

通过以上步骤,你可以利用预训练的 ResNet-18 模型进行迁移学习,将其应用于不同的图像分类任务中。这样可以节省训练时间,并提升小数据集任务上的性能。

5.可视化模型预测

用于显示一些图像预测的通用函数

def visualize_model(model, num_images=6):was_training = model.trainingmodel.eval()images_so_far = 0fig = plt.figure()with torch.no_grad():for i, (inputs, labels) in enumerate(dataloaders['val']):inputs = inputs.to(device)labels = labels.to(device)outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size()[0]):images_so_far += 1ax = plt.subplot(num_images//2, 2, images_so_far)ax.axis('off')ax.set_title(f'predicted: {class_names[preds[j]]}')imshow(inputs.cpu().data[j])if images_so_far == num_images:model.train(mode=was_training)returnmodel.train(mode=was_training)

6.卷积神经网络微调

加载预训练模型并重新配置最终的全连接层。

model_ft = models.resnet18(weights='IMAGENET1K_V1')
num_ftrs = model_ft.fc.in_features
# Here the size of each output sample is set to 2.
# Alternatively, it can be generalized to ``nn.Linear(num_ftrs, len(class_names))``.
model_ft.fc = nn.Linear(num_ftrs, 2)model_ft = model_ft.to(device)criterion = nn.CrossEntropyLoss()# Observe that all parameters are being optimized
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)

运行后会下载resnet18-f37072fd。pth模型用于预训练,输出如下:

Downloading: "https://download.pytorch.org/models/resnet18-f37072fd.pth" to /var/lib/ci-user/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth0%|          | 0.00/44.7M [00:00<?, ?B/s]47%|####7     | 21.0M/44.7M [00:00<00:00, 219MB/s]95%|#########5| 42.6M/44.7M [00:00<00:00, 223MB/s]
100%|##########| 44.7M/44.7M [00:00<00:00, 221MB/s]

7.评估和训练

model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,num_epochs=25)

在这段代码中,model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=25) 调用了 train_model 函数,并传递了几个参数。每个参数在这里都有其特定的作用和含义:

  1. model_ft:

    • 含义: 这是要训练的模型(在这里是经过预训练的 ResNet-18 模型)。model_ft 包含了模型的架构和当前的权重。
    • 作用: 该模型会在训练过程中被更新(优化),以更好地适应特定任务(如蜜蜂和蚂蚁的分类)。
  2. criterion:

    • 含义: 损失函数(在这里是交叉熵损失函数 CrossEntropyLoss)。
    • 作用: 损失函数用于衡量模型的预测结果与实际标签之间的差异。模型的目标是最小化这个损失,以提高预测的准确性。
  3. optimizer_ft:

    • 含义: 优化器(在这里是随机梯度下降优化器 SGD)。
    • 作用: 优化器负责更新模型的权重,以最小化损失函数的输出。通过调整学习率和动量,优化器能够帮助模型更快地收敛到最优解。
  4. exp_lr_scheduler:

    • 含义: 学习率调度器(在这里是 StepLR 调度器)。
    • 作用: 学习率调度器用于在训练过程中逐步降低学习率。这样可以帮助模型在训练的后期以较小的步伐调整权重,从而更精细地调整模型的参数,提高模型的最终性能。在这里,学习率每过 7 个 epoch 会按照 gamma=0.1 的因子进行衰减。
  5. num_epochs=25:

    • 含义: 训练的轮次数量。
    • 作用: 这个参数决定了训练的总轮次。在这里,模型将会被训练 25 个 epoch(每个 epoch 包含一次完整的训练集和验证集的前向传播和反向传播)。

运行代码后开始训练,输出如下:

在这里插入图片描述

对模型结果进行评估,得到下面结果:

visualize_model(model_ft)

在这里插入图片描述

visualize_model 函数的主要目的是展示模型在验证集(或测试集)上对图像的预测结果。通过查看模型对几个样本图像的预测,你可以直观地理解模型的表现,判断它是否能够正确识别图像中的对象。

8.神经网络固定特征提取

下面这段代码,我们需要冻结除最后一层之外的所有网络,设置冻结参数,以便不再计算梯度。requires_grad = Falsebackward()

model_conv = torchvision.models.resnet18(weights='IMAGENET1K_V1')
for param in model_conv.parameters():param.requires_grad = False# Parameters of newly constructed modules have requires_grad=True by default
num_ftrs = model_conv.fc.in_features
model_conv.fc = nn.Linear(num_ftrs, 2)model_conv = model_conv.to(device)criterion = nn.CrossEntropyLoss()# Observe that only parameters of final layer are being optimized as
# opposed to before.
optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)

这段代码加载了一个使用 IMAGENET1K_V1 权重预训练的 ResNet-18 模型,冻结了除最后一层全连接层以外的所有层的参数,并将最后一层替换为一个适应二分类任务(蜜蜂和蚂蚁)的全连接层,然后设置优化器只更新最后一层的参数。

Q:为什么要使这些参数在训练中保持不变?

A:在迁移学习中,通常会冻结预训练模型的大部分参数,只训练最后几层(如最后一层全连接层)。这是因为预训练模型(例如使用 IMAGENET1K_V1 权重的 ResNet-18)已经在一个大规模的数据集(ImageNet-1000)上学习到了非常通用的特征,这些特征能够很好地表示图像中的低级和中级信息(如边缘、纹理、形状等)。
通过冻结这些层的参数,可以:

  1. 减少训练时间和计算资源:冻结大部分层减少了需要更新的参数数量,因此训练速度更快,资源消耗更低。
  2. 避免过拟合:预训练的特征已经被证明是有效的,通过只训练最后一层,可以防止模型过度拟合到新的、小规模的数据集上。
  3. 利用通用特征:前几层学习到的特征是通用的,适用于多种任务,通过保留这些特征,可以提高模型在新任务上的表现。

这种方法有效地将预训练模型的强大特征提取能力与新任务的特定需求结合起来,从而实现更好的模型性能。
在这里插入图片描述

训练并评估模型

model_conv = train_model(model_conv, criterion, optimizer_conv,exp_lr_scheduler, num_epochs=25)visualize_model(model_conv)plt.ioff()
plt.show()

运行结果如下:
在这里插入图片描述

9.自定义测试集测试

将需要识别的图片放在test_img文件夹中,并将测试图片路径赋值给img_path,模型路径赋值于model调用visualize_model_predictions函数实现对自定义图片的识别

def visualize_model_predictions(model,img_path):was_training = model.trainingmodel.eval()img = Image.open(img_path)img = data_transforms['val'](img)img = img.unsqueeze(0)img = img.to(device)with torch.no_grad():outputs = model(img)_, preds = torch.max(outputs, 1)ax = plt.subplot(2,2,1)ax.axis('off')ax.set_title(f'Predicted: {class_names[preds[0]]}')imshow(img.cpu().data[0])model.train(mode=was_training)visualize_model_predictions(model_conv,img_path='data/hymenoptera_data/val/bees/72100438_73de9f17af.jpg')plt.ioff()
plt.show()

运行代码后可以看到图像被正确的识别,结果如下:

在这里插入图片描述

10.结论

微调(fine-tuning)和使用卷积神经网络(ConvNet)作为固定特征提取器是两个不同的方法,尽管它们都用于迁移学习。它们的关系可以理解为:

1. 微调(Fine-tuning)

微调是迁移学习的一种策略,其中一个预训练的模型在新的数据集上进行进一步训练。微调的主要步骤包括:

  • 加载预训练模型:通常从一个大数据集(如ImageNet)上训练的模型开始。
  • 替换输出层:将模型的最后一层(通常是全连接层)替换为适合新任务的层。例如,若原始模型是为1000类分类任务设计的,而你需要进行2类分类任务,则需要替换为一个具有2个输出单元的全连接层。
  • 训练:在新任务的数据集上训练模型时,可以选择是否训练整个网络的所有层,或者只训练新的全连接层。微调通常会解冻一些原本被冻结的层,并对这些层进行训练。
2. 使用固定特征提取器(Fixed Feature Extractor)

固定特征提取器是一种简单的迁移学习方法,其中预训练模型的特征提取部分被冻结,只有新添加的分类层(全连接层)会被训练。具体步骤包括:

  • 加载预训练模型:从一个大数据集上训练的模型开始。
  • 冻结特征提取层:将模型中除最后一层外的所有卷积层设置为不可训练(requires_grad=False)。
  • 添加新的分类层:在冻结的特征提取器之后添加一个新的全连接层,用于进行新的分类任务。
  • 训练:仅训练新的全连接层,固定的卷积层部分不进行训练。

总结

  • 微调固定特征提取器不是相互独立的,而是两种迁移学习的策略。微调是更灵活的方法,能够调整整个网络(或者大部分网络),适应新任务。固定特征提取器则是一种较为简单的方法,仅训练新的分类层,同时保持特征提取部分不变。

  • 顺序结构:在实际应用中,可以先使用固定特征提取器策略,然后逐步转向微调。如果固定特征提取器的结果不尽如人意,可以尝试微调以进一步提高性能。

  • 相互独立:这两个方法在实现上是相互独立的,但可以根据具体需求选择其一或者组合使用。

四.全部代码

说明:

  • flag == 1:进行微调,训练整个模型。
  • flag == 2:使用固定特征提取器,仅训练新加的分类层。
  • flag == 3:使用训练好的模型进行预测
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import torch.backends.cudnn as cudnn
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
from PIL import Image
from tempfile import TemporaryDirectory#选择操作
flag = 1def main():cudnn.benchmark = Trueplt.ion()  # interactive mode# Data augmentation and normalization for training# Just normalization for validationdata_transforms = {'train': transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),'val': transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),}data_dir = 'hymenoptera_data'image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),data_transforms[x])for x in ['train', 'val']}dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,shuffle=True, num_workers=4)for x in ['train', 'val']}dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}class_names = image_datasets['train'].classesdevice = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")def imshow(inp, title=None):"""Display image for Tensor."""inp = inp.numpy().transpose((1, 2, 0))mean = np.array([0.485, 0.456, 0.406])std = np.array([0.229, 0.224, 0.225])inp = std * inp + meaninp = np.clip(inp, 0, 1)plt.imshow(inp)if title is not None:plt.title(title)plt.pause(0.001)  # pause a bit so that plots are updated# Get a batch of training datainputs, classes = next(iter(dataloaders['train']))# Make a grid from batchout = torchvision.utils.make_grid(inputs)# 模型训练def train_model(model, criterion, optimizer, scheduler, num_epochs=25, model_name="best_model_params.pt"):since = time.time()# 创建目录以保存模型if not os.path.exists('model'):os.makedirs('model')best_model_params_path = os.path.join('model', model_name)best_acc = 0.0for epoch in range(num_epochs):print(f'Epoch {epoch}/{num_epochs - 1}')print('-' * 10)# Each epoch has a training and validation phasefor phase in ['train', 'val']:if phase == 'train':model.train()  # Set model to training modeelse:model.eval()  # Set model to evaluate moderunning_loss = 0.0running_corrects = 0# Iterate over data.for inputs, labels in dataloaders[phase]:inputs = inputs.to(device)labels = labels.to(device)# zero the parameter gradientsoptimizer.zero_grad()# forward# track history if only in trainwith torch.set_grad_enabled(phase == 'train'):outputs = model(inputs)_, preds = torch.max(outputs, 1)loss = criterion(outputs, labels)# backward + optimize only if in training phaseif phase == 'train':loss.backward()optimizer.step()# statisticsrunning_loss += loss.item() * inputs.size(0)running_corrects += torch.sum(preds == labels.data)if phase == 'train':scheduler.step()epoch_loss = running_loss / dataset_sizes[phase]epoch_acc = running_corrects.double() / dataset_sizes[phase]print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')# deep copy the modelif phase == 'val' and epoch_acc > best_acc:best_acc = epoch_acctorch.save(model.state_dict(), best_model_params_path)print()time_elapsed = time.time() - sinceprint(f'Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s')print(f'Best val Acc: {best_acc:4f}')# load best model weightsmodel.load_state_dict(torch.load(best_model_params_path))return model# 可视化模型预测def visualize_model(model, num_images=6):was_training = model.trainingmodel.eval()images_so_far = 0fig = plt.figure()with torch.no_grad():for i, (inputs, labels) in enumerate(dataloaders['val']):inputs = inputs.to(device)labels = labels.to(device)outputs = model(inputs)_, preds = torch.max(outputs, 1)for j in range(inputs.size()[0]):images_so_far += 1ax = plt.subplot(num_images // 2, 2, images_so_far)ax.axis('off')ax.set_title(f'predicted: {class_names[preds[j]]}')imshow(inputs.cpu().data[j])if images_so_far == num_images:model.train(mode=was_training)returnmodel.train(mode=was_training)imshow(out, title=[class_names[x] for x in classes])# 微调特征提取器if flag == 1:model_ft = models.resnet18(weights='IMAGENET1K_V1')num_ftrs = model_ft.fc.in_featuresmodel_ft.fc = nn.Linear(num_ftrs, 2)model_ft = model_ft.to(device)criterion = nn.CrossEntropyLoss()optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=25,model_name="finetuned_model_params.pt")visualize_model(model_ft)# 固定特征提取器elif flag == 2:model_conv = torchvision.models.resnet18(weights='IMAGENET1K_V1')for param in model_conv.parameters():param.requires_grad = Falsenum_ftrs = model_conv.fc.in_featuresmodel_conv.fc = nn.Linear(num_ftrs, 2)model_conv = model_conv.to(device)criterion = nn.CrossEntropyLoss()optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)model_conv = train_model(model_conv, criterion, optimizer_conv, exp_lr_scheduler, num_epochs=25,model_name="fixed_model_params.pt")visualize_model(model_conv)plt.ioff()plt.show()# 使用训练好的模型识别elif flag == 3:model_conv = models.resnet18()num_ftrs = model_conv.fc.in_featuresmodel_conv.fc = nn.Linear(num_ftrs, 2)model_conv = model_conv.to(device)model_conv.load_state_dict(torch.load('model/finetuned_model_params.pt'))def visualize_model_predictions(model, img_path):was_training = model.trainingmodel.eval()img = Image.open(img_path).convert("RGB")img = data_transforms['val'](img)img = img.unsqueeze(0)img = img.to(device)with torch.no_grad():outputs = model(img)_, preds = torch.max(outputs, 1)ax = plt.subplot(2, 2, 1)ax.axis('off')ax.set_title(f'Predicted: {class_names[preds[0]]}')imshow(img.cpu().data[0])model.train(mode=was_training)visualize_model_predictions(model_conv,img_path='D:/Python_Code_WorkSpace/BeesOrAnts_DL/test_img/40708249_1415445497609.jpg')plt.ioff()plt.show()if __name__ == '__main__':main()

写在最后

在这篇文章中,我们深入探讨了卷积神经网络(CNN)迁移学习的基本概念和实用技巧,这些知识是理解和实现端到端智能驾驶系统的基础。端到端智能驾驶系统旨在通过一个统一的深度学习框架,直接将传感器数据映射到驾驶决策,从而简化传统的多阶段处理流程。在未来的研究中,结合BEV(鸟瞰视角)图像和Transformer模型的先进方法正在成为热门趋势,它们可以有效提升对复杂驾驶场景的理解和处理能力。掌握CNN迁移学习将为进一步深入这些前沿技术打下坚实的基础,

加油,汽车人!

相关文章:

基于CNN卷积神经网络迁移学习的图像识别实现

基于CNN卷积神经网络迁移学习的图像识别实现 基于CNN卷积神经网络迁移学习的图像识别实现写在前面一&#xff0c;原理介绍迁移学习的基本方法1.样本迁移&#xff08;Instance based TL&#xff09;2.特征迁移&#xff08;Feature based TL&#xff09;3.模型迁移&#xff08;Pa…...

【iOS】push和present的区别

【iOS】push和present的区别 文章目录 【iOS】push和present的区别前言pushpop presentdismiss简单小demo来展示dismiss和presentdismiss多级 push和present的区别区别相同点 前言 在iOS开发中&#xff0c;我们经常性的会用到界面的一个切换的问题&#xff0c;这里我们需要理清…...

在Linux服务器上添加用户并设置自动登录

需要在Linux服务器上添加一个新用户&#xff0c;可以使用以下命令 # 这个命令会创建一个新的用户账户&#xff0c;默认情况下不会设置密码&#xff0c;不会在 /home 目录下为新用户创建home目录&#xff1a; # sudo useradd 用户名 # # 如果希望同时为新用户创建家目录&#…...

网站被爬,数据泄露,如何应对不断强化的安全危机?

近年来&#xff0c;众多传统零售商和互联网企业借助大数据、人工智能等先进技术手段&#xff0c;通过场景化设计、优化客户体验、融合线上线下渠道&#xff0c;推动了网络电商行业的消费方式变革&#xff0c;成为电商领域新的增长动力。 但值得注意的是&#xff0c;网络电商带来…...

为什么HTTPS会引入SSL/TLS协议

这时我面试遇到过的问题,整理了一下,希望对大家有帮助! 祝大家秋招顺利! 首先 SSL/TLS 协议通过使用数字证书来实现服务器身份认证, 当用户访问一个 HTTPS 网站时,浏览器会验证服务器的数字证书, 1.首先他对验证整证书是否在有效期 2.其次他会看证书中的服务器域名…...

Spring AOP,通知使用,spring事务管理,spring_web搭建

spring AOP AOP概述 AOP面向切面编程是对面向对象编程的延续&#xff08;AOP &#xff08;Aspect Orient Programming&#xff09;,直译过来就是 面向切面编程,AOP 是一种编程思想&#xff0c;是面向对象编程&#xff08;OOP&#xff09;的一种补充。&#xff09; 面向切面编…...

PHP无缝对接预订无忧场馆预订系统小程序源码

无缝对接&#xff0c;预订无忧 —— 场馆预订系统&#xff0c;让每一次活动都完美启航&#xff01; 一、告别繁琐流程&#xff0c;预订从未如此简单 你是否曾经为了预订一个合适的场馆而焦头烂额&#xff1f;繁琐的咨询、确认、支付流程&#xff0c;让人心力交瘁。但现在&…...

Unet改进30:添加CAA(2024最新改进方法)|上下文锚定注意模块来捕获远程上下文信息。

本文内容:在不同位置添加CAA注意力机制 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 遥感图像中的目标检测经常面临一些日益严峻的挑战,包括目标尺度的巨大变化和不同的测距环境。先前的方法试图通过大核卷积或扩展卷积来扩展主干的空间感受野来解决这…...

OpenAI震撼发布最强模型o1!强化学习突破LLM推理极限

OpenAI新模型无预警上新&#xff1a; o1系列&#xff0c;可以进行通用复杂推理&#xff0c;每次回答要花费更长时间思考。 在解决博士水平的物理问题时&#xff0c;GPT-4o还是“不及格”59.5分&#xff0c;o1一跃来到“优秀档”&#xff0c;直接干到92.8分&#xff01; 没错…...

速通GPT-2:Language Models are Unsupervised Multitask Learners全文解读

文章目录 GPT系列论文速通引言总览GPT和GPT-2区别Abstract1. 概括2. 具体分析 Introduction1. 概括2. 具体分析当前机器学习系统的局限性希望构建通用型系统数据集与任务通用性缺乏的原因 Approach1. 概括与要点2. 原文阅读翻译3. 具体分析论文核心Training DatasetInput Repre…...

Python 最小公倍数计算器:从基础到应用

目录 引言数学背景 什么是最小公倍数(LCM)计算LCM的方法Python基础 Python简介Python安装和设置使用Python计算最小公倍数 理论基础Python实现详细代码解析 辅助函数LCM计算函数最小公倍数的应用 工作中的应用场景日常生活中的应用场景优化与扩展 代码优化处理多个数字进阶话…...

网络学习-eNSP配置路由器

#PC1网关&#xff1a;192.168.1.254 #PC3网关&#xff1a;192.168.3.254 #PC4网关&#xff1a;192.168.4.254# 注&#xff1a;路由器接口必须配置不同网段IP地址 <Huawei>system-view Enter system view, return user view with CtrlZ. #给路由器两个接口配置IP地址 [Hua…...

在 React 中,如何使用 Context API 来实现跨组件的通信?

在 React 中&#xff0c;Context API 提供了一种方式&#xff0c;允许你在组件树中传递数据&#xff0c;而无需在每个层级手动传递 props。这对于实现跨组件通信非常有用&#xff0c;特别是当你需要在多个组件间共享状态或函数时。 以下是如何使用 Context API 来实现跨组件通…...

【基础算法总结】位运算

目录 一&#xff0c;常见位运算操作总结二&#xff0c;算法原理和代码实现191.位1的个数338.比特位计数461.汉明距离面试题01.01.判断字符是否唯一268.丢失的数字371.两整数之和136.只出现一次的数字137.只出现一次的数字II260.只出现一次的数据III面试题17.19.消失的两个数字 …...

组件通信——provide 和 inject 实现爷孙组件通信

provide 和 inject 实现爷孙组件通信 介绍 provide 和 inject 是 Vue.js 提供的一种在组件之间共享数据的机制&#xff0c;它允许在组件树中的任何地方注入依赖项。这对于跨越多个层级的组件间通信特别有用&#xff0c;因此无需手动将 prop 数据逐层传递下去。 provide&#…...

【ShuQiHere】探索人工智能核心:机器学习的奥秘

【ShuQiHere】 &#x1f4a1; 什么是机器学习&#xff1f; 机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;Artificial Intelligence, AI&#xff09;中最关键的组成部分之一。它使得计算机不仅能够处理数据&#xff0c;还能从数据中学习&#x…...

LeeCode打卡第二十四天

LeeCode打卡第二十四天 第一题&#xff1a;对称二叉树&#xff08;LeeCode第101题&#xff09;: 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …...

什么是科技与艺术相结合的异形创意圆形(饼/盘)LED显示屏

在当今数字化与创意并重的时代&#xff0c;科技与艺术的融合已成为推动社会进步与文化创新的重要力量。其中&#xff0c;晶锐创显异形创意圆形LED显示屏作为这一趋势下的杰出代表&#xff0c;不仅打破了传统显示设备的形态束缚&#xff0c;更以其独特的造型、卓越的显示效果和广…...

AI大模型知识点大梳理_ai大模型知识学习,零基础入门到精通,收藏这一篇就够了

文章目录 AI大模型是什么AI大模型发展历程AI大模型的底层原理AI大模型解决的问题大模型的优点和不足影响个人观点 AI大模型是什么 AI大模型是指具有巨大参数量的深度学习模型&#xff0c;通常包含数十亿甚至数万亿个参数。这些模型可以通过学习大量的数据来提高预测能力&…...

NVG040W语音芯片:为制氧机带来个性化语音提示和报警功能

在当今社会&#xff0c;家庭医疗设备和健康保健产品越来越受到人们的关注。制氧机作为其中的一种&#xff0c;为许多需要氧气治疗的人们提供了重要的帮助。然而&#xff0c;对于许多用户来说&#xff0c;如何正确操作和维护这些设备仍然是一个挑战。为此&#xff0c;NVG040W语音…...

OpenCV结构分析与形状描述符(12)椭圆拟合函数fitEllipseAMS()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆&#xff0c;该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[260]提出的近…...

安卓显示驱动

安卓显示驱动是用于在Android设备上提供图形和视频显示的底层软件组件。 显示驱动在Android系统中扮演着至关重要的角色&#xff0c;它们负责将图形和视频内容从系统内存传输到显示屏上。这些驱动程序确保了用户界面、图像、视频和游戏等视觉元素的正常显示。以下是关于安卓显…...

java重点学习-集合(List)

七 集合&#xff08;List&#xff09; 7.1 复杂度分析 7.2 数组 1.数组(Array)是一种用连续的内存空间存储相同数据类型 数据的线性数据结构。 2.数组下标为什么从0开始 寻址公式是:baseAddressi*dataTypeSize&#xff0c;计算下标的内存地址效率较高 3.查找的时间复杂度 随机(…...

【PCB测试】最常见的PCB测试方法

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 一、PCB测试的好处1.发现错误2.降低成本3.节省时间4.减少退货率5.提高安全性 二、PCB测试内容1.孔壁质量2.电镀铜3.清…...

AtCoder Beginner Contest 370 ABCD题详细题解(C++,Python)

前言: 本文为AtCoder Beginner Contest 370 ABCD题的详细题解&#xff0c;包含C,Python语言描述&#xff0c;觉得有帮助或者写的不错可以点个赞 个人感觉D比C简单&#xff0c;C那里的字典序有点不理解, E应该是前缀和加dp&#xff0c;但是是dp不明白&#xff0c;等我明白了会更…...

斯坦福研究人员探讨大型语言模型在社交网络生成中的应用及其在政治同质性上的偏见

社交网络生成在许多领域有着广泛的应用&#xff0c;比如流行病建模、社交媒体模拟以及理解社交现象如两极化等。当由于隐私问题或其他限制无法直接观察真实网络时&#xff0c;创建逼真的社交网络就显得尤为重要。这些生成的网络对于在这些情况下准确建模互动和预测结果至关重要…...

一招教你找到Facebook广告的最佳发帖时间

在社交媒体上做广告时&#xff0c;时机是至关重要的。有时候你投放的广告参与度低&#xff0c;很有可能是因为你没有在适当的时机投放广告。这篇文章会教你如何找到适合自己的广告投放时间&#xff0c;如果你感兴趣的话&#xff0c;就继续看下去吧&#xff01; 首先&#xff0…...

【数据库】MySQL-基础篇-多表查询

专栏文章索引&#xff1a;数据库 有问题可私聊&#xff1a;QQ&#xff1a;3375119339 目录 一、多表关系 1.一对多 2.多对多 3.一对一 二、多表查询概述 1.数据准备 2.概述 3.分类 三、内连接 1.隐式内连接 2.显式内连接 3.案例 四、外连接 1.左外连接 2.右外连…...

MongoDB事务机制

事务机制 1.事务概念 在对数据的操作的过程中&#xff0c;涉及到一连串的操作&#xff0c;这些操作如果失败&#xff0c;会导致我们的数据部分变化了&#xff0c;部分没变化。这个过程就好比如你去吃早餐&#xff0c;你点完餐了&#xff0c;并且吃完早餐了&#xff0c;没付钱你…...

大模型 LLM(Large Language Models)如今十分火爆,对于初入此领域的新人小白来说,应该如何入门 LLM 呢?是否有值得推荐的入门教程呢?

前言 很明显&#xff0c;这是一个偏学术方向的指南要求&#xff0c;所以我会把整个LLM应用的从数学到编程语言&#xff0c;从框架到常用模型的学习方法&#xff0c;给你捋一个通透。也可能是不爱学习的劝退文。 通常要达到熟练的进行LLM相关的学术研究与开发&#xff0c;至少…...