02极简LLM逻辑与PyTorch快速入门

文章目录

02极简LLM逻辑与PyTorch快速入门

极简LLM逻辑

提供一段伪代码,说明"对话"的基本逻辑:输入、输出。

不考虑机器学习、深度学习之类的技术,也不考虑结果是否合理、准确,仅仅为了说明LLM基本逻辑。

python 复制代码
if __name__ == "__main__":
    aq = InputAndAnalysisQuestion()
    ga = Get_answer()
    while True:
        question = input('请输入:') 
        index, params = aq.query_question(question)
        answers = ga.get_data(index, params)
        print('答案:')
        for ans in answers:
            print(ans[0])

按照俗话解释以上伪代码流程:

  • 1、获得对话输入。根据输入内容,进行查询检索;
  • 2、上一步查询检索返回索引号、参数列表数据;
  • 3、根据索引号、参数列表计算回答内容;
  • 4、输出打印。

PyTorch环境安装(重要,不难)

在安装PyTorch前,请先确保python、cuda的版本。具体的版本对应关系参考pytorch官网给出的说明。

官网版本对应:https://pytorch.org/get-started/locally/

  • 例如:
软件 版本
python >=3.8
cuda 11.8
pytorch 2.2.1
  • 注意,上面描述的是pytorch的GPU环境的安装。pytorch也支持无GPU的CPU环境安装,具体步骤请自行查阅。但是还是建议上GPU吧,GPU是绕不过去的。

  • PyTorch历史版本,用于查阅与python、cuda的版本对照

    地址:https://pytorch.org/get-started/previous-versions/

    例如:

python 复制代码
# CUDA 11.8
conda install pytorch==2.2.0 torchvision==0.17.0 torchaudio==2.2.0 pytorch-cuda=11.8 -c pytorch -c nvidia
# CUDA 12.1
conda install pytorch==2.2.0 torchvision==0.17.0 torchaudio==2.2.0 pytorch-cuda=12.1 -c pytorch -c nvidia
# CPU Only
conda install pytorch==2.2.0 torchvision==0.17.0 torchaudio==2.2.0 cpuonly -c pytorch
  • torch版本查询

    import torch
    torch.version

  • GPU/cuda环境判断与启用

    We move our tensor to the GPU if available

    if torch.cuda.is_available():
    tensor = tensor.to('cuda')
    print(f"Device tensor is stored on: {tensor.device}")

PyTorch 主要概念

Tensors张量

张量是一种特殊的数据结构,与数组和矩阵非常相似。

在PyTorch中,使用张量来编码模型的输入、输出以及模型参数。张量类似于NumPy中的ndarrays(n维数组),但不同之处在于张量可以在GPU或其他硬件加速器上运行。

张量和NumPy数组经常可以共享相同的底层内存,从而消除了数据复制的需求。张量还针对自动微分进行了优化(在Autograd部分)

import torch
import numpy as np
张量常见的形式:scalar、vector、matrix、n-dimensinal
  • 0:scalar

  • 1:vector

  • 2:matrix

  • 3:n-dimensional

    import torch
    from torch import tensor

    #scalar通常是一个数值
    x = tensor(42.)

    tensor(42.)

    x.dim()

    tensor(84.)

    x.item()

    #vector, 例如: [-5., 2., 0.],在深度学习中通常指特征,例如词向量特征
    v = tensor( [1.5, -0.5, 3.0])

    v.dim()

    v.size()
    torch.Size( [3] )

    #Matrix, 一般计算的都是矩阵,通常都是多维的
    M = tensor( [[1., 2.], [3., 4.]] )

    M.matmul(M)

    tensor([1., 0.]).matmul(M)

张量初始化
  • 数据直接读取

    data = [[1, 2],[3, 4]]
    x_data = torch.tensor(data)

  • 读取NumPy array

    np_array = np.array(data)
    x_np = torch.from_numpy(np_array)

  • 从另一个张量

    x_ones = torch.ones_like(x_data) # retains the properties of x_data
    print(f"Ones Tensor: \n {x_ones} \n")

    x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data
    print(f"Random Tensor: \n {x_rand} \n")

  • 随机变量

    shape = (2,3,)
    rand_tensor = torch.rand(shape)
    ones_tensor = torch.ones(shape)
    zeros_tensor = torch.zeros(shape)

    print(f"Random Tensor: \n {rand_tensor} \n")
    print(f"Ones Tensor: \n {ones_tensor} \n")
    print(f"Zeros Tensor: \n {zeros_tensor}")

张量参数:shape、datatype、device
tensor = torch.rand(3,4)

print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
张量运算

超过100种张量操作,包括算术运算、线性代数、矩阵操作(转置、索引、切片)、采样以及其他更多操作。

每一种操作都可以在GPU上运行(通常速度会比在CPU上更快)。

默认情况下,张量是在CPU上创建的。需要通过.to()方法显式地将张量移动到GPU(在确认GPU可用之后)。请注意,跨设备复制大型张量在时间和内存方面可能会产生较高的开销!

  • 索引与切片(类似于numpy)

    tensor = torch.ones(4, 4)
    print(f"First row: {tensor[0]}")
    print(f"First column: {tensor[:, 0]}")
    print(f"Last column: {tensor[..., -1]}")
    tensor[:,1] = 0
    print(tensor)

  • 连接张量 torch.cat

    t1 = torch.cat([tensor, tensor, tensor], dim=1)
    print(t1)

  • 算术运算

    This computes the matrix multiplication between two tensors. y1, y2, y3 will have the same value

    tensor.T returns the transpose of a tensor

    y1 = tensor @ tensor.T
    y2 = tensor.matmul(tensor.T)

    y3 = torch.rand_like(y1)
    torch.matmul(tensor, tensor.T, out=y3)

    This computes the element-wise product. z1, z2, z3 will have the same value

    z1 = tensor * tensor
    z2 = tensor.mul(tensor)

    z3 = torch.rand_like(tensor)
    torch.mul(tensor, tensor, out=z3)

  • 单元素张量
    如果有一个单元素张量,例如通过聚合一个张量的所有值为一个值,可以使用item()方法将其转换为Python数值类型

    agg = tensor.sum()
    agg_item = agg.item()
    print(agg_item, type(agg_item))

  • 原地操作(In-place operations)
    把结果直接存入操作数本身的运算称为原地操作。这类操作的名称后面通常会带有 _ 后缀。例如:x.copy_(y)、x.t_() 等操作会直接修改变量x自身的内容。

    print(f"{tensor} \n")
    tensor.add_(5)
    print(tensor)

Datasets and DataLoaders 数据集与数据加载

处理数据样本的代码可能会变得混乱且难以维护;为了提高可读性和模块化程度,理想情况下希望数据集代码与模型训练代码解耦。

PyTorch提供了两个数据处理基本组件:

torch.utils.data.DataLoader 和 torch.utils.data.Dataset,既可使用预加载的数据集,也能使用自定义数据。

Dataset 类用于存储样本及其对应的标签,而 DataLoader 则将一个可迭代对象包装在 Dataset 外部,以便于轻松访问样本。

PyTorch领域库提供了一系列预加载的数据集(如FashionMNIST等),这些数据集都是torch.utils.data.Dataset的子类,并实现了针对特定数据的特定函数。可以利用它们快速原型化和基准测试您的模型。

  • 数据加载
    Fashion-MNIST是一个由Zalando提供的商品图片数据集,包含60,000个训练样本和10,000个测试样本。每个样本由一个28×28像素的灰度图像以及来自10种类别的关联标签组成。

    import torch
    from torch.utils.data import Dataset
    from torchvision import datasets
    from torchvision.transforms import ToTensor
    import matplotlib.pyplot as plt

    training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
    )

    test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
    )

  • 迭代与可视化数据集

    labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
    }
    figure = plt.figure(figsize=(8, 8))
    cols, rows = 3, 3
    for i in range(1, cols * rows + 1):
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[label])
    plt.axis("off")
    plt.imshow(img.squeeze(), cmap="gray")
    plt.show()

  • 从文件创建自定义数据集

    import os
    import pandas as pd
    from torchvision.io import read_image

    class CustomImageDataset(Dataset):
    def init(self, annotations_file, img_dir, transform=None, target_transform=None):
    self.img_labels = pd.read_csv(annotations_file)
    self.img_dir = img_dir
    self.transform = transform
    self.target_transform = target_transform

      def __len__(self):
          return len(self.img_labels)
    
      def __getitem__(self, idx):
          img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
          image = read_image(img_path)
          label = self.img_labels.iloc[idx, 1]
          if self.transform:
              image = self.transform(image)
          if self.target_transform:
              label = self.target_transform(label)
          return image, label
    
  • 使用DataLoader加载数据准备训练

数据集一次检索一个样本的特征和标签。

在训练模型时,我们通常希望以"小批量"的方式传递样本,每轮迭代时重新打乱数据以减少模型过拟合现象,并利用Python的多进程功能来加速数据获取。

from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)

# Display image and label.
train_features, train_labels = next(iter(train_dataloader))
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

Transforms 转换

原始数据并不总是以机器学习算法所需的理想形式出现。因此,会采用转换技术对数据进行一些操作,使其适合训练过程。

所有TorchVision数据集都具有两个参数

------transform用于修改特征,target_transform用于修改标签

------这两个参数接受包含转换逻辑的可调用对象。

torchvision.transforms模块直接提供了几种常用的转换方法。

FashionMNIST数据集的特征是以PIL Image格式表示的,标签则是整数形式。为了训练,需要将特征转化为归一化的张量,并将标签转化为独热编码的张量。为此,将使用ToTensor和Lambda来进行这些转换操作。

import torch
from torchvision import datasets
from torchvision.transforms import ToTensor, Lambda

ds = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
    target_transform=Lambda(lambda y: torch.zeros(10, dtype=torch.float).scatter_(0, torch.tensor(y), value=1))
)

Build Model 创建模型

神经网络由执行数据操作的层/模块组成。

PyTorch中的torch.nn命名空间提供了构建自定义神经网络所需的所有基本组件。

PyTorch中的每一个模块都是nn.Module的子类。一个神经网络本身就是一个模块,它包含了其他模块(即层)。这种嵌套结构使得构建和管理复杂的架构变得容易。

我们将构建一个神经网络来对FashionMNIST数据集中的图像进行分类。

import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

device = (
    "cuda"
    if torch.cuda.is_available()
    else "mps"
    if torch.backends.mps.is_available()
    else "cpu"
)
print(f"Using {device} device")

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits
        
model = NeuralNetwork().to(device)
print(model)

Automatic Differentiation 自动微分

在训练神经网络时,最常使用的算法是反向传播。该算法中,会根据损失函数相对于给定参数的梯度来调整参数(即模型权重)。

为了计算这些梯度,PyTorch 内置了一个名为 torch.autograd 的自动微分引擎。它可以支持任何计算图的梯度自动计算。

考虑一个最简单的单层神经网络,其包含输入x、参数w和b以及某个损失函数。在PyTorch中,可以如下方式定义这个网络:

import torch

x = torch.ones(5)  # input tensor
y = torch.zeros(3)  # expected output
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
z = torch.matmul(x, w)+b
loss = torch.nn.functional.binary_cross_entropy_with_logits(z, y)

loss.backward()
print(w.grad)
print(b.grad)

Optimization Loop 优化循环

有了模型和数据,接下来需要通过在数据上优化模型参数来训练、验证和测试我们的模型。

训练模型是一个迭代过程,在每次迭代中,模型都会对输出做出猜测,计算猜测的误差(损失),收集关于模型参数的误差梯度(正如我们在上一节中看到的那样),然后使用梯度下降法优化这些参数。

import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

train_dataloader = DataLoader(training_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

model = NeuralNetwork()

def train_loop(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)
    # Set the model to training mode - important for batch normalization and dropout layers
    # Unnecessary in this situation but added for best practices
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        # Compute prediction and loss
        pred = model(X)
        loss = loss_fn(pred, y)

        # Backpropagation
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()

        if batch % 100 == 0:
            loss, current = loss.item(), batch * batch_size + len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")


def test_loop(dataloader, model, loss_fn):
    # Set the model to evaluation mode - important for batch normalization and dropout layers
    # Unnecessary in this situation but added for best practices
    model.eval()
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0

    # Evaluating the model with torch.no_grad() ensures that no gradients are computed during test mode
    # also serves to reduce unnecessary gradient computations and memory usage for tensors with requires_grad=True
    with torch.no_grad():
        for X, y in dataloader:
            pred = model(X)
            test_loss += loss_fn(pred, y).item()
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    correct /= size
    print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
    
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)

epochs = 10
for t in range(epochs):
    print(f"Epoch {t+1}\n-------------------------------")
    train_loop(train_dataloader, model, loss_fn, optimizer)
    test_loop(test_dataloader, model, loss_fn)
print("Done!")

保存、加载、使用模型

import torch
import torchvision.models as models

model = models.vgg16(weights='IMAGENET1K_V1')
torch.save(model.state_dict(), 'model_weights.pth')

model.load_state_dict(torch.load('model_weights.pth'))
model.eval()
相关推荐
一点媛艺2 小时前
Kotlin函数由易到难
开发语言·python·kotlin
qzhqbb2 小时前
基于统计方法的语言模型
人工智能·语言模型·easyui
冷眼看人间恩怨3 小时前
【话题讨论】AI大模型重塑软件开发:定义、应用、优势与挑战
人工智能·ai编程·软件开发
2401_883041083 小时前
新锐品牌电商代运营公司都有哪些?
大数据·人工智能
魔道不误砍柴功3 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
_.Switch3 小时前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
AI极客菌4 小时前
Controlnet作者新作IC-light V2:基于FLUX训练,支持处理风格化图像,细节远高于SD1.5。
人工智能·计算机视觉·ai作画·stable diffusion·aigc·flux·人工智能作画
阿_旭4 小时前
一文读懂| 自注意力与交叉注意力机制在计算机视觉中作用与基本原理
人工智能·深度学习·计算机视觉·cross-attention·self-attention
王哈哈^_^4 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
测开小菜鸟5 小时前
使用python向钉钉群聊发送消息
java·python·钉钉