小菜鸟

java菜鸟号正在起航

Python 虚拟环境:别再把所有项目塞进一个”大仓库”了

你有没有遇到过这种情况:

项目 A 用的是 Django 2.0,项目 B 用的是 Django 4.0。你在项目 B 里 pip install django==4.0,结果项目 A 启动就报错。改回去呢,项目 B 又跑不起来。

这就是典型的”依赖冲突”——所有项目共用一套全局 Python 环境,不同项目对同一个库的不同版本要求互相打架。

虚拟环境解决的就是这个问题:每个项目有自己的独立环境,各用各的版本,互不干扰。

虚拟环境是什么?打个比方

把你电脑上的 Python 想象成一个”公共图书馆”(全局环境)。所有项目都从这个图书馆借书(第三方库)。

  • 项目 A 要借 Django 2.0
  • 项目 B 要借 Django 4.0
  • 图书馆里只能放一个版本的书

虚拟环境就是给每个项目盖了一个独立的”小书房”——项目 A 的书房里有 Django 2.0,项目 B 的书房里有 Django 4.0,互不影响。

全局 Python 环境(系统)
├── 项目 A 的虚拟环境
│   ├── Django 2.0
│   └── requests 2.28
└── 项目 B 的虚拟环境
    ├── Django 4.0
    └── numpy 1.24

核心好处:

  • 每个项目的依赖互不干扰
  • 系统 Python 环境保持干净
  • 通过 requirements.txt 可以一键复现环境
  • 不同项目可以用不同 Python 版本

核心工具:venv(Python 3.3+ 自带,不用装)

venv 是 Python 官方自带的虚拟环境工具,最轻量、最通用、最适合新手。不需要额外安装,直接用。

1. 创建虚拟环境

cd my_project
python -m venv .venv
阅读全文 »

PyTorch 张量完全指南:深度学习的核心数据结构

核心认知:张量(Tensor)是深度学习世界的”通用语言”——无论是图像、文本、语音还是视频,都会被转换为张量进行处理。理解张量的创建、变换和运算,是掌握 PyTorch 的第一步,也是构建神经网络的基础。

张量是 PyTorch 中最基本的数据结构,类似于 NumPy 的 ndarray,但增加了 GPU 加速和自动求导等深度学习特性。本文将系统讲解张量的核心概念、创建方式、维度变换和常用运算。


目录

  1. 什么是张量?
  2. 创建张量
  3. 张量属性
  4. 索引与切片
  5. 维度变换(核心重点)
  6. 张量运算
  7. 设备管理(CPU/GPU)
  8. 广播机制
  9. 实战案例
  10. 总结速查表

什么是张量?

从标量到高阶张量

张量是标量、向量、矩阵的泛化,是 AI 中统一的数据格式。

阶 (Rank) 名称 数学表示 示例 形状 (Shape)
0 标量 (Scalar) 单个数字 5 ()
1 向量 (Vector) 一维数组 [1, 2, 3] (3,)
2 矩阵 (Matrix) 二维表格 [[1, 2], [3, 4]] (2, 2)
3 3阶张量 三维数组 彩色图片 (高, 宽, 通道) (224, 224, 3)
4 4阶张量 四维数组 一批图片 (批次, 高, 宽, 通道) (32, 224, 224, 3)
5 5阶张量 五维数组 视频数据 (批次, 帧, 通道, 高, 宽) (16, 30, 3, 224, 224)

张量的三个核心属性

每个张量都有三个核心属性:

import torch

tensor = torch.rand(3, 4)

print(f"形状 (shape): {tensor.shape}")      # torch.Size([3, 4])
print(f"数据类型 (dtype): {tensor.dtype}")  # torch.float32
print(f"设备 (device): {tensor.device}")    # cpu
属性 说明 常见取值
shape 每个维度的大小 (3, 224, 224)
dtype 元素类型 torch.float32, torch.int64, torch.bool
device 存储位置 cpu, cuda:0

创建张量

从已有数据创建

import torch
import numpy as np

# 1. 从 Python 列表创建(最常用)
tensor_list = torch.tensor([[1, 2], [3, 4]])
print(tensor_list)
# tensor([[1, 2],
#         [3, 4]])

# 2. 从 NumPy 数组创建(共享内存)
np_array = np.array([1, 2, 3])
tensor_numpy = torch.from_numpy(np_array)
print(tensor_numpy)  # tensor([1, 2, 3])

# 3. 指定数据类型
tensor_float = torch.tensor([1, 2, 3], dtype=torch.float32)
tensor_int = torch.tensor([1.5, 2.7], dtype=torch.int64)  # 小数会被截断为 [1, 2]

创建特殊值的张量

# 全零张量
zeros = torch.zeros(2, 3)      # shape: (2, 3)
print(zeros)
# tensor([[0., 0., 0.],
#         [0., 0., 0.]])

# 全一张量
ones = torch.ones(2, 3)        # shape: (2, 3)

# 单位矩阵
eye = torch.eye(3)             # shape: (3, 3)
print(eye)
# tensor([[1., 0., 0.],
#         [0., 1., 0.],
#         [0., 0., 1.]])

# 填充指定值
full = torch.full((2, 3), 7)   # shape: (2, 3),所有元素为 7

# 空张量(未初始化,值随机,速度快)
empty = torch.empty(2, 3)

创建随机张量

# 均匀分布 [0, 1)
rand_uniform = torch.rand(2, 3)       # shape: (2, 3)

# 标准正态分布 N(0, 1)
rand_normal = torch.randn(2, 3)       # shape: (2, 3)

# 均匀分布 [low, high)
rand_uniform_range = torch.empty(2, 3).uniform_(0, 10)

# 随机整数 [low, high)
rand_int = torch.randint(0, 10, (2, 3))  # shape: (2, 3)

# 正态分布(可指定均值和标准差)
normal = torch.normal(mean=0, std=1, size=(2, 3))

创建序列张量

# arange:类似 Python 的 range
arange_1 = torch.arange(5)           # tensor([0, 1, 2, 3, 4])
arange_2 = torch.arange(1, 10, 2)    # tensor([1, 3, 5, 7, 9])

# linspace:等间隔
linspace = torch.linspace(0, 1, 5)   # tensor([0.0000, 0.2500, 0.5000, 0.7500, 1.0000])

# logspace:对数等间隔
logspace = torch.logspace(0, 2, 5)   # tensor([1., 3.16, 10., 31.6, 100.])

与 NumPy 互转

# NumPy → Tensor
np_array = np.array([1, 2, 3])
tensor = torch.from_numpy(np_array)   # 共享内存

# Tensor → NumPy
tensor = torch.tensor([1, 2, 3])
np_array = tensor.numpy()             # 共享内存

#  警告:共享内存意味着修改一个会影响另一个
tensor = torch.tensor([1, 2, 3])
np_array = tensor.numpy()
tensor[0] = 100
print(np_array)  # [100, 2, 3] ← 被修改了!

张量属性

查看和修改属性

tensor = torch.rand(3, 4)

# 查看形状
print(tensor.shape)          # torch.Size([3, 4])
print(tensor.size())         # torch.Size([3, 4])
print(tensor.numel())        # 12(元素总数)

# 查看数据类型
print(tensor.dtype)          # torch.float32

# 修改数据类型
tensor_int = tensor.to(torch.int64)
tensor_float16 = tensor.half()        # 转为 float16
tensor_float32 = tensor.float()       # 转为 float32

# 查看设备
print(tensor.device)         # cpu

# 查看是否需要梯度(自动求导)
print(tensor.requires_grad)  # False
tensor.requires_grad_(True)  # 启用梯度追踪

形状的表示

# torch.Size 可以像元组一样使用
shape = torch.rand(2, 3, 4).shape
print(len(shape))      # 3(维度数)
print(shape[0])        # 2
print(shape[1])        # 3
print(shape[2])        # 4

# 获取维度数
dim = tensor.dim()     # 等价于 len(tensor.shape)

索引与切片

基础索引

tensor = torch.arange(24).reshape(4, 6)
print(tensor)
# tensor([[ 0,  1,  2,  3,  4,  5],
#         [ 6,  7,  8,  9, 10, 11],
#         [12, 13, 14, 15, 16, 17],
#         [18, 19, 20, 21, 22, 23]])

# 获取单个元素
element = tensor[1, 2]        # 第2行第3列 → 8

# 获取一行
first_row = tensor[0]         # 第一行 → [0, 1, 2, 3, 4, 5]

# 获取一列
first_col = tensor[:, 0]      # 第一列 → [0, 6, 12, 18]

# 获取子矩阵
sub = tensor[1:3, 2:4]        # 行1~2,列2~3
# tensor([[ 8,  9],
#         [14, 15]])

# 步长切片
every_other = tensor[::2, ::2]  # 每隔一行/一列取一个
# tensor([[ 0,  2,  4],
#         [12, 14, 16]])

高级索引

tensor = torch.arange(12).reshape(3, 4)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])

# 整数索引(取特定位置的元素)
indices = tensor[[0, 2], [1, 3]]  # 取 (0,1) 和 (2,3) → tensor([1, 11])

# 布尔掩码(取满足条件的元素)
mask = tensor > 5
selected = tensor[mask]           # tensor([ 6,  7,  8,  9, 10, 11])

# 使用 where 进行条件替换
result = torch.where(tensor > 5, tensor, torch.zeros_like(tensor))
# tensor([[0, 0, 0, 0],
#         [0, 0, 6, 7],
#         [8, 9, 10, 11]])

维度变换(核心重点)

reshape 与 view

tensor = torch.arange(12)  # shape: (12,)

# reshape:改变形状(自动处理内存连续性)
reshaped = tensor.reshape(3, 4)       # shape: (3, 4)
reshaped = tensor.reshape(2, 2, 3)    # shape: (2, 2, 3)
reshaped = tensor.reshape(-1, 4)      # shape: (3, 4)(-1 表示自动计算)

# view:类似 reshape,但要求内存连续
viewed = tensor.view(3, 4)            # shape: (3, 4)

#  区别:view 要求内存连续,reshape 更灵活
tensor = torch.rand(3, 4).t()  # 转置后内存不连续
# tensor.view(2, 6)  #  可能报错
tensor.reshape(2, 6)           #  可以工作

squeeze 与 unsqueeze

# squeeze:移除大小为 1 的维度
tensor = torch.rand(1, 3, 1, 4)    # shape: (1, 3, 1, 4)
squeezed = tensor.squeeze()        # shape: (3, 4) 移除所有 size=1 的维度
squeezed_dim = tensor.squeeze(0)   # shape: (3, 1, 4) 只移除维度0
squeezed_dim = tensor.squeeze(2)   # shape: (1, 3, 4) 只移除维度2

# unsqueeze:添加大小为 1 的维度
tensor = torch.rand(3, 4)          # shape: (3, 4)
unsqueezed = tensor.unsqueeze(0)   # shape: (1, 3, 4) 在第0维前添加
unsqueezed = tensor.unsqueeze(1)   # shape: (3, 1, 4) 在第1维前添加
unsqueezed = tensor.unsqueeze(-1)  # shape: (3, 4, 1) 在最后添加

# 💡 实用场景:批量处理时添加 batch 维度
single_image = torch.rand(224, 224, 3)   # shape: (224, 224, 3)
batch_image = single_image.unsqueeze(0)  # shape: (1, 224, 224, 3)

transpose 与 permute

# transpose:交换两个维度
matrix = torch.tensor([[1, 2], [3, 4]])   # shape: (2, 2)
transposed = matrix.t()                   # 转置,shape: (2, 2)
transposed = torch.transpose(matrix, 0, 1) # 交换维度0和1

# permute:重排多个维度(更灵活)
tensor = torch.rand(2, 3, 4)              # shape: (2, 3, 4)

# 将 (batch, height, width) → (batch, width, height)
permuted = tensor.permute(0, 2, 1)        # shape: (2, 4, 3)

# 图像格式转换:(batch, height, width, channel) → (batch, channel, height, width)
image = torch.rand(32, 224, 224, 3)       # NHWC 格式
image_nchw = image.permute(0, 3, 1, 2)    # NCHW 格式,shape: (32, 3, 224, 224)

维度变换速查表

操作 代码 输入形状 输出形状 说明
展平 tensor.flatten() (2, 3, 4) (24,) 一维化
展平(保留batch) tensor.flatten(1) (32, 3, 224, 224) (32, 3×224×224) 保留第一维
reshape tensor.reshape(4, 6) (24,) (4, 6) 任意改变形状
增维 tensor.unsqueeze(0) (3, 4) (1, 3, 4) 在第0维添加
降维 tensor.squeeze() (1, 3, 1, 4) (3, 4) 移除size=1的维度
转置 tensor.t() (3, 4) (4, 3) 2D转置
交换维度 tensor.transpose(0, 1) (2, 3, 4) (3, 2, 4) 交换指定两维
重排 tensor.permute(1, 2, 0) (2, 3, 4) (3, 4, 2) 任意重排顺序

张量运算

基本算术运算(逐元素)

a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

# 加减乘除
print(a + b)   # tensor([5, 7, 9])
print(a - b)   # tensor([-3, -3, -3])
print(a * b)   # tensor([4, 10, 18])   逐元素乘法,不是矩阵乘法!
print(a / b)   # tensor([0.2500, 0.4000, 0.5000])

# 幂运算
print(a ** 2)       # tensor([1, 4, 9])
print(torch.pow(a, 2))  # 同上

# 平方根
print(torch.sqrt(a))  # tensor([1.0000, 1.4142, 1.7321])

# 指数/对数
print(torch.exp(a))   # tensor([2.7183, 7.3891, 20.0855])
print(torch.log(a))   # tensor([0.0000, 0.6931, 1.0986])

矩阵乘法

# 矩阵乘法要求:A的列数 = B的行数
# 公式:(m, n) × (n, p) → (m, p)

A = torch.rand(2, 3)   # shape: (2, 3)
B = torch.rand(3, 4)   # shape: (3, 4)

# 方式1:@ 运算符(推荐)
C = A @ B              # shape: (2, 4)

# 方式2:torch.matmul
C = torch.matmul(A, B) # shape: (2, 4)

# 方式3:torch.mm(仅适用于2D)
C = torch.mm(A, B)     # shape: (2, 4)

# 批量矩阵乘法(3D+)
batch_A = torch.rand(16, 2, 3)  # (batch, m, n)
batch_B = torch.rand(16, 3, 4)  # (batch, n, p)
batch_C = torch.bmm(batch_A, batch_B)  # (16, 2, 4)

聚合操作

tensor = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32)

# 求和
print(tensor.sum())           # 21.0(所有元素)
print(tensor.sum(dim=0))      # tensor([5., 7., 9.])(按列)
print(tensor.sum(dim=1))      # tensor([6., 15.])(按行)

# 均值
print(tensor.mean())          # 3.5
print(tensor.mean(dim=0))     # tensor([2.5, 3.5, 4.5])

# 最大值/最小值
print(tensor.max())           # 6.0
print(tensor.max(dim=1))      # 返回值 (values, indices)

# 其他聚合
print(tensor.prod())          # 720(乘积)
print(tensor.std())           # 1.7078(标准差)
print(tensor.var())           # 2.9167(方差)

# 保持维度的聚合(keepdim=True)
sum_keepdim = tensor.sum(dim=1, keepdim=True)  # shape: (2, 1)

设备管理(CPU/GPU)

基本设备操作

import torch

# 检查 CUDA 是否可用
print(torch.cuda.is_available())   # True/False
print(torch.cuda.device_count())   # GPU 数量
print(torch.cuda.get_device_name(0))  # GPU 名称

# 创建设备对象
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 在指定设备上创建张量
tensor_on_cpu = torch.rand(3, 4)                     # 默认 CPU
tensor_on_gpu = torch.rand(3, 4, device="cuda")     # 直接在 GPU 创建
tensor_on_device = torch.rand(3, 4, device=device)  # 使用设备对象

# 移动张量到设备
tensor = torch.rand(3, 4)
tensor = tensor.to(device)          # 移动到目标设备
tensor = tensor.cuda()              # 移动到 GPU
tensor = tensor.cpu()               # 移动到 CPU

# 检查张量所在设备
print(tensor.device)                # cpu 或 cuda:0

模型与数据设备对齐

import torch.nn as nn

# 创建模型和数据
model = nn.Linear(10, 2)
data = torch.rand(32, 10)

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# 将模型和数据移到同一设备
model = model.to(device)
data = data.to(device)

# 推理
output = model(data)  # 输出也在 GPU 上

广播机制

当两个张量形状不同时,PyTorch 会自动尝试广播(Broadcasting),使它们形状兼容。

广播规则

  1. 从尾部维度开始对齐
  2. 如果两个维度大小相等,或其中一个为 1,或其中一个不存在,则可以广播
  3. 否则无法广播
# 示例1:标量广播
a = torch.rand(3, 4)
b = 2
c = a * b  # 2 被广播为 (3, 4) 形状

# 示例2:向量广播到矩阵
a = torch.rand(3, 4)
b = torch.rand(4)
c = a + b  # b 被广播为 (3, 4)

# 示例3:列向量广播
a = torch.rand(3, 4)
b = torch.rand(3, 1)
c = a + b  # b 被广播为 (3, 4)

# 示例4:无法广播的情况
a = torch.rand(3, 4)
b = torch.rand(2, 4)
# c = a + b  #  形状不兼容!(3,4) vs (2,4)

# 两列都不相同,但是都存在1
a = torch.rand(3, 1)
b = torch.rand(1, 3)
c = a + b  # b 被广播为 (3, 3)

手动广播

# expand:复制数据(不复制内存,高效)
a = torch.rand(3, 1)
expanded = a.expand(3, 4)   # 形状 (3, 4)

# repeat:实际复制数据(消耗内存)
repeated = a.repeat(1, 4)   # 形状 (3, 4)

实战案例

案例1:图像数据预处理(NHWC ↔ NCHW)

import torch

# 模拟一批图像:batch_size=32, height=224, width=224, channels=3
images_nhwc = torch.rand(32, 224, 224, 3)

print(f"原始形状: {images_nhwc.shape}")  # (32, 224, 224, 3)

# PyTorch 模型通常需要 NCHW 格式
images_nchw = images_nhwc.permute(0, 3, 1, 2)

print(f"转换后形状: {images_nchw.shape}")  # (32, 3, 224, 224)

# 归一化(假设像素值范围 0-255)
images_normalized = images_nchw / 255.0

# 标准化(mean=0.5, std=0.5)
mean = torch.tensor([0.5, 0.5, 0.5]).view(1, 3, 1, 1)
std = torch.tensor([0.5, 0.5, 0.5]).view(1, 3, 1, 1)
images_standardized = (images_normalized - mean) / std

案例2:批量矩阵计算

# 场景:计算多个向量的点积
batch_size = 100
vector_dim = 256

# 生成两批向量
A = torch.rand(batch_size, vector_dim)
B = torch.rand(batch_size, vector_dim)

# 方式1:逐元素相乘后求和
dot_products = (A * B).sum(dim=1)  # shape: (100,)

# 方式2:使用 einsum(更清晰)
dot_products = torch.einsum('ij,ij->i', A, B)  # shape: (100,)

# 场景:注意力分数计算(Q · K^T)
Q = torch.rand(32, 8, 64)   # (batch, heads, dim)
K = torch.rand(32, 8, 64)   # (batch, heads, dim)
scores = torch.einsum('bhd,bhd->bh', Q, K)  # (batch, heads)

案例3:掩码操作

# 创建序列数据(模拟 padding)
seq_lengths = [3, 5, 2, 4]
max_len = max(seq_lengths)
batch_size = len(seq_lengths)

# 创建 attention mask
mask = torch.zeros(batch_size, max_len, dtype=torch.bool)
for i, length in enumerate(seq_lengths):
    mask[i, :length] = 1

print(mask)
# tensor([[ True,  True,  True, False, False],
#         [ True,  True,  True,  True,  True],
#         [ True,  True, False, False, False],
#         [ True,  True,  True,  True, False]])

# 使用 mask 过滤
data = torch.rand(batch_size, max_len, 10)
masked_data = data[mask]  # 只保留有效位置的数据

案例4:创建神经网络层

import torch.nn as nn

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
        self.fc = nn.Linear(32 * 56 * 56, 10)
    
    def forward(self, x):
        # x shape: (batch, 3, 224, 224)
        x = self.conv1(x)           # (batch, 16, 224, 224)
        x = nn.ReLU()(x)
        x = nn.MaxPool2d(2)(x)      # (batch, 16, 112, 112)
        
        x = self.conv2(x)           # (batch, 32, 112, 112)
        x = nn.ReLU()(x)
        x = nn.MaxPool2d(2)(x)      # (batch, 32, 56, 56)
        
        x = x.view(x.size(0), -1)   # (batch, 32*56*56)
        x = self.fc(x)              # (batch, 10)
        return x

# 创建模型
model = SimpleCNN()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

# 前向传播
x = torch.rand(16, 3, 224, 224).to(device)
output = model(x)
print(output.shape)  # (16, 10)

总结:张量操作速查表

操作类型 常用函数 说明
创建 torch.tensor(), torch.zeros(), torch.ones(), torch.rand() 创建张量
转换 .numpy(), torch.from_numpy() 与 NumPy 互转
形状 .shape, .size(), .numel() 查看形状和大小
重塑 .reshape(), .view(), .flatten() 改变形状
增/删维度 .unsqueeze(), .squeeze() 操作 size=1 的维度
重排 .permute(), .transpose() 交换/重排维度
索引 tensor[1, 2], tensor[mask] 获取子集
运算 +, -, *, /, @ 基本运算和矩阵乘法
聚合 .sum(), .mean(), .max() 统计聚合
设备 .to(device), .cuda(), .cpu() 移动张量

深度学习 vs 机器学习:核心区别与选型指南

核心认知:深度学习是机器学习的子集,它们的关系是:人工智能 > 机器学习 > 深度学习。两者最本质的区别在于特征工程的方式——机器学习依赖人工提取特征,深度学习则自动从数据中学习特征层次。

深度学习和机器学习常常被混淆。本文将用最直观的方式,从核心原理、数据需求、硬件依赖、适用场景等多个维度,帮你彻底理清两者的关系,并在实际项目中做出正确选择。


目录

  1. 概念层级:AI > ML > DL
  2. 核心区别:特征工程
  3. 多维度对比
  4. 通俗比喻
  5. 典型应用场景
  6. 如何选择?决策指南
  7. 进阶:何时从 ML 升级到 DL

概念层级:AI > ML > DL

┌─────────────────────────────────────────────────────────────┐
│                      人工智能 (AI)                          │
│  让机器模仿人类智能的广义概念                                 │
│                                                             │
│  ┌───────────────────────────────────────────────────────┐  │
│  │               机器学习 (Machine Learning)              │  │
│  │  让机器从数据中学习规律,无需显式编程                    │  │
│  │                                                        │  │
│  │  ┌─────────────────────────────────────────────────┐  │  │
│  │  │           深度学习 (Deep Learning)               │  │  │
│  │  │  使用多层神经网络自动提取特征的机器学习子集        │  │  │
│  │  └─────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
层级 定义 示例
人工智能 (AI) 让机器模仿人类智能的广义概念 专家系统、机器人、自然语言处理
机器学习 (ML) AI的子集,让机器从数据中学习规律 线性回归、决策树、支持向量机
深度学习 (DL) ML的子集,使用多层神经网络的算法 卷积神经网络(CNN)、循环神经网络(RNN)、Transformer

核心区别:特征工程

特征工程是机器学习和深度学习最本质的区别——即如何从原始数据中提取有意义的特征。

阅读全文 »

机器学习三大范式:监督学习、无监督学习与强化学习完全指南

核心认知:机器学习不是魔法,而是“从数据中学习规律”的科学。想象你在教一个孩子认识水果——监督学习是直接告诉答案,无监督学习是让孩子自己发现规律,强化学习则是通过奖励和惩罚引导孩子试错。这三种范式构成了现代人工智能的基石。

机器学习是人工智能的核心领域,而监督学习、无监督学习和强化学习是其三大支柱。本文将通过生动的比喻、详细的对比和实际案例,帮助你彻底理解这三种学习范式。


目录

  1. 监督学习:有答案的“填鸭式教学”
  2. 无监督学习:无答案的“自主探索”
  3. 强化学习:靠反馈的“试错式成长”
  4. 三者对比总结
  5. 实际应用场景矩阵
  6. 延伸阅读:半监督学习与自监督学习

监督学习:有答案的“填鸭式教学”

核心逻辑

监督学习使用带标签的数据进行训练——每个训练样本都包含输入特征和对应的正确输出(标签)。模型学习输入到输出的映射关系,然后对未见过的数据进行预测。

训练数据:(特征, 标签) → 模型 → 预测新数据

水果比喻

家长直接教孩子:“这是苹果——圆形、红/绿色;这是香蕉——长条形、黄色”。孩子学会后,看到一个新的圆形红色水果,就能判断“这是苹果”。

工作流程

┌─────────────────────────────────────────────────────────┐
│                      监督学习流程                        │
├─────────────────────────────────────────────────────────┤
│  历史数据(带标签)                                      │
│  ┌─────────┬──────────┬─────────┐                      │
│  │ 形状    │ 颜色     │ 标签    │                      │
│  ├─────────┼──────────┼─────────┤                      │
│  │ 圆形    │ 红色     │ 苹果    │ ──┐                  │
│  │ 长条形  │ 黄色     │ 香蕉    │   │                  │
│  │ 圆形    │ 绿色     │ 苹果    │   │                  │
│  └─────────┴──────────┴─────────┘   │                  │
│                                      ↓                  │
│                              训练机器学习模型            │
│                                      │                  │
│                                      ↓                  │
│  新数据 ──→ 模型预测 ──→ 预测结果(苹果/香蕉)          │
└─────────────────────────────────────────────────────────┘

主要任务类型

阅读全文 »

大模型架构演变:从n-gram到Transformer的双雄争霸与融合

在人工智能的宏大叙事中,大语言模型(LLM)的崛起无疑是近年来最激动人心的篇章。从最初笨拙的统计模型到如今能写诗、编程、推理的智能体,这场技术革命并非一蹴而就,而是一场关于“如何理解语言”的深刻探索。

我们就来梳理这条从统计概率到神经网络,再到Transformer架构分化,直至2026年形成“一超多强”格局的演进之路。

统计时代的基石:n-gram模型

在深度学习尚未统治自然语言处理(NLP)领域的时代,语言模型主要依赖于统计学方法。其中最经典的就是n-gram模型。它的核心思想非常直观:一个词出现的概率,仅取决于它前面的n-1个词。

例如,在二元模型(Bigram)中,要预测句子“我爱人工…”后面的词,模型只关注“人工”这个词,计算P(智能|人工)的概率。这种方法简单有效,曾广泛应用于早期的机器翻译和输入法联想中。然而,n-gram有着天然的缺陷:它无法捕捉长距离的依赖关系(比如“我出生在法国,…我会说流利的法语”中,“法国”与“法语”的关联),且面临着严重的数据稀疏问题——只要语料库中没出现过的词组,概率就是零。

我们可以用一段简单的Python代码来模拟n-gram的统计过程:

from collections import Counter, defaultdict

# 模拟一个简单的语料库
corpus = "I love artificial intelligence. I love coding."
words = corpus.split()

# 构建二元模型(Bigram)统计字典
bigram_model = defaultdict(list)
for i in range(len(words) - 1):
    current_word = words[i]
    next_word = words[i+1]
    bigram_model[current_word].append(next_word)

# 预测:给定 "love",下一个词最可能是什么?
current_word = "love"
possible_next_words = bigram_model[current_word]
# 统计出现频率
prediction = Counter(possible_next_words).most_common(1)[0][0]

print(f"给定单词 '{current_word}',模型预测下一个词是:'{prediction}'")
# 输出: 给定单词 'love',模型预测下一个词是:'artificial'

神经网络的觉醒:从Word2Vec到RNN

随着计算能力的提升,研究者们开始尝试用神经网络来建模语言。2003年,Bengio提出了神经概率语言模型,首次将神经网络引入语言建模。随后,2013年Google推出的Word2Vec更是里程碑式的突破,它将词语映射为稠密的向量(词嵌入),使得“国王 - 男人 + 女人 ≈ 女王”这样的语义运算成为可能。

阅读全文 »
0%