PyTorch深度学习实战 |手算​​变分自编码器(VAE)

本文涉及的产品
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: 本文详解变分自编码器(VAE)原理:指出传统自编码器因潜在空间无序而无法生成新图像;VAE通过引入概率建模,用高斯分布近似后验,并结合重构损失与KL散度优化,使潜在空间连续可采样,从而实现可控图像生成。含公式推导、重参数化技巧及完整代码实现。(239字)

 引入

为什么传统的自编码器不能生成新的图像呐?

    传统自编码器接收图像后,会将其压缩为 10-100 维的低维向量,再通过该向量重建原始图像。

这种低维向量所处的空间更紧凑、易解释,被称为潜在空间,即便原始图像可能包含上千个像素。

image.gif 编辑

现在咱们有个训练好的解码器,它能根据图像的潜在表示图重建出来。那要生成新图,很自

然就会想:从潜在空间里随便挑些点,再让解码器跑一遍不就行了?但这里有个问题 —— 大部分

时候这么做没用。因为潜在空间通常没什么规律,乱糟糟的,所以空间里大部分区域,解码器根本

没法把它变成有意义的图像。

image.gif 编辑

另一种方法可能是取一种编码后的图像,然后采样其潜在表示附近的点。这应该会给我们原始图像

附近的图像。这样也不完全的正确,潜在空间的表示很差,以至于附近的点也不能对应原始图像附

近有意义的变化。如果我们采样接近原始图像的点,我们可能只会得到原始图像相同的重构

image.gif 编辑


原理介绍

      那么解决方案是什么啊?理想情况下,我们希望有一个组织良好的潜在空间。在那里采样点会

导致连贯的新图像。下面让我们看看VAE是如何做到的。

     下面快速说下贝叶斯的基本符号和属性。假设有个随机变量 X,它能取 0 到 10 之间的任何

值;如果某个事件的发生概率是按 X 的分布来的,就叫 “从 X 里抽样”。还有个叫 “概率密度函数”

的东西,用 p (x) 表示,它能告诉我们从 X 里抽到 0 到 10 中某个值的可能性有多大。另外,E (x)

代表这个分布的 “期望值”,简单说就是从 X 里抽很多次,这些结果的平均大概是多少,这个平均

值能通过概率密度函数的积分算出来。

image.gif 编辑

现在看 X 和 Z,3D 图里的是它们的联合概率分布,意思就是每对可能的 X 和 Z 同时发生的概率。

当然 X 和 Z 各自也有自己的概率分布,叫边缘概率分布,分别用 P (X) 和 P (Z) 表示。有意思的

是,用联合分布能算出这两个边缘分布,这个过程叫边缘化,得对另一个变量积分才行 —— 比如

想知道 X 取某个值的概率,就把所有可能的 Z 值代入联合分布积分;想知道 Z 取某个值的概率,

就把所有可能的 X 值代入联合分布积分。还有个概念叫条件概率,其实就是从联合分布里 “切出一

片”,再用 Z 值抽样的概率做归一化处理。

image.gif 编辑

我们的目标是从数据集(比如图像)的分布 P (x) 里生成新数据,但我们不知道 P (x) 具体长什么

样,只能拿到一些样本。所以我们引入了低维空间的潜在分布 P (Z),用 Z 向量捕捉数据核心特

征;为了连接 P (x) 和 P (Z),需要两个映射后验分布(看图像 X 生成 Z 的概率)似然分布

(看 Z 重构出 X 的概率)—— 理论上从后验抽 Z 再重构成 X,就能生成新数据,但问题是我们也

不知道 P (Z) 的具体形式,计算根本没法做。于是我们假设 P (Z) 是正态分布,这样就能算似然 P

(X|Z) 了,可还是缺后验 P (Z|X);这时候变分自编码器就用一个带可学习参数的高斯分布 q (z|x)

来近似真实后验,这个学习过程就是变分贝叶斯优化

image.gif 编辑

我们用自编码器从图像里估计两个参数,再让解码器根据抽样出的潜在变量 Z 重构图像。要实现

自编码器近似后验并重构图像,得先通过贝叶斯公式推导出训练目标 —— 这个目标由两部分组

成,第一部分是 “一致性项”,用来衡量用 Z 重构原图像 X 的效果,因为我们做了假设,它能简化

成 L2 损失(均方误差),算的时候只要用解码器重构图像,再对比重构图和原图的 L2 差距就

行;第二部分是 KL 散度,它能测近似后验和先验分布 P (Z) 的距离,而我们选的 P (Z) 是正态分

布,所以优化时能让近似后验也接近正态分布。总的来说,变分自编码器的训练目标 L 就是带正

则化的重构损失,既靠 L2 损失保证重构效果,又靠 KL 散度让潜在空间符合正态分布,最终确保

生成的样本和原始数据分布 p (x) 一致。

image.gif 编辑


手动计算

网络结构

     现在咱们从理论转到实际,自编码器是这么实现这些分布的:普通自编码器输入压缩成潜在

空间的一个点,再解码回输入空间,但变分自编码器不一样 —— 它的编码器会把输入转换成一个

高斯分布的两个参数(均值和方差),用这个分布来代表输入,而不是一个点;之后从这个高斯分

布里随机抽点让解码器把这些点转回输入空间,就能算 L 损失的各个部分,再反向传播优化。不

过抽样操作本身没法反向传播,这就需要重新参数化技巧:先从标准正态分布里抽个随机点,用编

码器输出的方差缩放它、用均值移动它,这样既相当于从后验分布抽样,又能让过程对参数可微

分,支持反向传播。典型的 VAE 流程和普通自编码器类似,先拿图像生成重构图,对比原图算重

构效果,但它还会输出后验分布的参数,再算这个后验和先验(标准正态分布)的 KL 散度,而两

个高斯分布的 KL 散度有现成的计算公式可以直接用。

image.gif 编辑

手算模拟

假设我们有这样一个任务:

输入特征 D=4 (例如:身高、体重、年龄、收入)

潜在空间 L=2 (例如:抽象的“健康度”和“财富值”)

  首先我们把输入展平,然后经过8个神经元的线性层,得到的(1*8)的输出,然后再经过一个激

活函数,得到(1*8)的输出。

image.gif 编辑

分别通过两个2个神经元的先行层,得到(1*2)的均值参数和方差的对数参数。

image.gif 编辑

通过重参数化得到潜在变量Z

image.gif 编辑

潜在变量Z,通过8个神经元的线性层,Relu激

BCE = F.mse_loss(recon_x, x.view(-1, D_INPUT), reduction='mean')  # 重构损失(MSE)
  KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())    # KL散度损失
  return BCE, KLD, BCE + KLD  # 分别返回各损失

image.gif

活函数,4个神经元的线性层,最终得到1*4的输出。

image.gif 编辑

损失函数计算

image.gif 编辑

代码实现

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"  # 放在所有库导入前
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# --- 设定参数 ---
D_INPUT = 4    # 输入维度
H_HIDDEN = 8   # 隐藏层维度
L_LATENT = 2   # 潜在空间维度
# 假设的输入数据 (D=4)
X_input = torch.tensor([4.0, 3.0, 1.0, 2.0], dtype=torch.float32).unsqueeze(0)
print("原始输入形状:", X_input.shape)  # 输出形状:[1, 4](批量大小=1,特征数=4)
# --- VAE 模型定义(增加中间结果返回) ---
class VAE(nn.Module):
    def __init__(self, D_in, H, L_lat):
        super(VAE, self).__init__()
        # 编码器层
        self.fc1 = nn.Linear(D_in, H)         # 输入→隐藏层
        self.fc_mu = nn.Linear(H, L_lat)      # 隐藏层→均值
        self.fc_logvar = nn.Linear(H, L_lat)  # 隐藏层→对数方差
        # 解码器层
        self.fc3 = nn.Linear(L_lat, H)        # 潜在变量→隐藏层
        self.fc4 = nn.Linear(H, D_in)         # 隐藏层→输出
        
        # 初始化权重(固定随机种子,保证结果可复现)
        nn.init.normal_(self.fc1.weight, mean=0.0, std=0.1)
        nn.init.constant_(self.fc1.bias, 0.0)
        nn.init.normal_(self.fc_mu.weight, mean=0.0, std=0.1)
        nn.init.constant_(self.fc_mu.bias, 0.0)
        nn.init.normal_(self.fc_logvar.weight, mean=0.0, std=0.1)
        nn.init.constant_(self.fc_logvar.bias, 0.0)
        nn.init.normal_(self.fc3.weight, mean=0.0, std=0.1)
        nn.init.constant_(self.fc3.bias, 0.0)
        nn.init.normal_(self.fc4.weight, mean=0.0, std=0.1)
        nn.init.constant_(self.fc4.bias, 0.0)
    def encode(self, x):
        # 编码器完整流程:输入→fc1→ReLU→输出均值和对数方差
        h1 = self.fc1(x)          # 第一层线性变换
        h1_act = F.relu(h1)       # ReLU激活
        mu = self.fc_mu(h1_act)   # 计算均值
        logvar = self.fc_logvar(h1_act)  # 计算对数方差
        return h1, h1_act, mu, logvar  # 返回中间结果
    def reparameterize(self, mu, logvar):
        std = torch.exp(0.5 * logvar)  # 标准差 = exp(0.5*log方差)
        eps = torch.randn_like(std)    # 从标准正态分布采样噪声
        z = mu + eps * std             # 重参数化得到潜在变量
        return std, eps, z
    def decode(self, z):
        # 解码器完整流程:潜在变量→fc3→ReLU→fc4→输出
        h3 = self.fc3(z)          # 第一层线性变换
        h3_act = F.relu(h3)       # ReLU激活
        recon_x = self.fc4(h3_act)  # 输出重构结果
        return h3, h3_act, recon_x  # 返回中间结果
    def forward(self, x):
        # 完整前向传播,返回所有中间结果
        h1, h1_act, mu, logvar = self.encode(x.view(-1, D_INPUT))
        std, eps, z = self.reparameterize(mu, logvar)
        h3, h3_act, recon_x = self.decode(z)
        return {
            'h1': h1, 'h1_act': h1_act,  # 编码器中间结果
            'mu': mu, 'logvar': logvar, 'std': std, 'eps': eps, 'z': z,  # 潜在空间相关
            'h3': h3, 'h3_act': h3_act,  # 解码器中间结果
            'recon_x': recon_x  # 最终重构结果
        }
def vae_loss_function(recon_x, x, mu, logvar):
    BCE = F.mse_loss(recon_x, x.view(-1, D_INPUT), reduction='mean')  # 重构损失(MSE)
    KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())    # KL散度损失
    return BCE, KLD, BCE + KLD  # 分别返回各损失
# --- 主程序(详细输出每一层结果) ---
if __name__ == "__main__":
    print("=" * 80)
    print("VAE 每一层详细计算过程")
    print("=" * 80)
    
    # 实例化模型
    model = VAE(D_INPUT, H_HIDDEN, L_LATENT)
    
    # 前向传播(获取所有中间结果)
    with torch.no_grad():  # 关闭梯度计算,仅做推理
        outputs = model(X_input)
        # 提取结果
        h1 = outputs['h1']
        h1_act = outputs['h1_act']
        mu = outputs['mu']
        logvar = outputs['logvar']
        std = outputs['std']
        eps = outputs['eps']
        z = outputs['z']
        h3 = outputs['h3']
        h3_act = outputs['h3_act']
        recon_x = outputs['recon_x']
        
        # 计算损失
        L_rec, L_kld, Total_Loss = vae_loss_function(recon_x, X_input, mu, logvar)
    
    # --------------------------
    # 1. 输入数据
    # --------------------------
    print("\n【1. 输入层】")
    print(f"输入数据 X (形状: {X_input.shape}): {X_input.squeeze().numpy().round(4)}")
    
    # --------------------------
    # 2. 编码器计算过程
    # --------------------------
    print("\n【2. 编码器】")
    print(f"  a. 输入→fc1线性变换 (形状: {h1.shape}): {h1.squeeze().numpy().round(4)}")
    print(f"  b. ReLU激活后 (形状: {h1_act.shape}): {h1_act.squeeze().numpy().round(4)}")
    print(f"  c. 均值 μ (形状: {mu.shape}): {mu.squeeze().numpy().round(4)}")
    print(f"  d. 对数方差 log(σ²) (形状: {logvar.shape}): {logvar.squeeze().numpy().round(4)}")
    
    # --------------------------
    # 3. 重参数化过程
    # --------------------------
    print("\n【3. 重参数化】")
    print(f"  a. 标准差 σ (形状: {std.shape}): {std.squeeze().numpy().round(4)}")
    print(f"  b. 随机噪声 ε (形状: {eps.shape}): {eps.squeeze().numpy().round(4)}")
    print(f"  c. 潜在变量 z = μ + ε×σ (形状: {z.shape}): {z.squeeze().numpy().round(4)}")
    
    # --------------------------
    # 4. 解码器计算过程
    # --------------------------
    print("\n【4. 解码器】")
    print(f"  a. z→fc3线性变换 (形状: {h3.shape}): {h3.squeeze().numpy().round(4)}")
    print(f"  b. ReLU激活后 (形状: {h3_act.shape}): {h3_act.squeeze().numpy().round(4)}")
    print(f"  c. 重构结果 X̂ (形状: {recon_x.shape}): {recon_x.squeeze().numpy().round(4)}")
    
    # --------------------------
    # 5. 损失计算
    # --------------------------
    print("\n【5. 损失计算】")
    print(f"  a. 重构损失 (MSE): {L_rec.item():.6f}")
    print(f"  b. KL散度损失: {L_kld.item():.6f}")
    print(f"  c. 总损失 (ELBO): {Total_Loss.item():.6f}")
    print("\n" + "=" * 80)

image.gif

image.gif 编辑

可视化

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"  # 放在所有库导入前
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# --- 设定参数 ---
D_INPUT = 4    # 输入维度
H_HIDDEN = 8   # 隐藏层维度
L_LATENT = 2   # 潜在空间维度
# 假设的输入数据 (D=4)
X_input = torch.tensor([4.0, 3.0, 1.0, 2.0], dtype=torch.float32).unsqueeze(0)
# --- VAE 模型定义 ---
class VAE(nn.Module):
    def __init__(self, D_in, H, L_lat):
        super(VAE, self).__init__()
        self.fc1 = nn.Linear(D_in, H)
        self.fc_mu = nn.Linear(H, L_lat)
        self.fc_logvar = nn.Linear(H, L_lat)
        self.fc3 = nn.Linear(L_lat, H)
        self.fc4 = nn.Linear(H, D_in)
    def encode(self, x):
        h1 = F.relu(self.fc1(x))
        return self.fc_mu(h1), self.fc_logvar(h1)
    def reparameterize(self, mu, logvar):
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std
    def decode(self, z):
        h3 = F.relu(self.fc3(z))
        return self.fc4(h3)
    def forward(self, x):
        mu, logvar = self.encode(x.view(-1, D_INPUT))
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar, z
def vae_loss_function(recon_x, x, mu, logvar):
    BCE = F.mse_loss(recon_x, x.view(-1, D_INPUT), reduction='mean')
    KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
    return BCE, KLD, BCE + KLD
# --- 潜在空间可视化函数 ---
def visualize_latent_space(mu, logvar, z_latent, num_samples=200):
    """可视化VAE的潜在空间分布"""
    fig, axes = plt.subplots(1, 2, figsize=(16, 7))
    
    # 计算标准差
    std = torch.exp(0.5 * logvar).squeeze().numpy()
    mu_np = mu.squeeze().numpy()
    z_np = z_latent.squeeze().numpy()
    
    # 生成多个采样点
    samples = []
    for _ in range(num_samples):
        eps = np.random.randn(L_LATENT)
        z_sample = mu_np + std * eps
        samples.append(z_sample)
    samples = np.array(samples)
    
    # 左图:VAE潜在空间分布
    axes[0].scatter(samples[:, 0], samples[:, 1], alpha=0.4, s=40, 
                   c='lightblue', edgecolors='blue', linewidth=0.5, label='采样点')
    axes[0].scatter(mu_np[0], mu_np[1], c='red', s=300, marker='*', 
                   edgecolors='darkred', linewidths=2, label='均值 μ', zorder=10)
    axes[0].scatter(z_np[0], z_np[1], c='purple', s=200, marker='D', 
                   edgecolors='darkviolet', linewidths=2, label='当前采样 z', zorder=10)
    
    # 绘制1σ和2σ椭圆
    ellipse_1sigma = Ellipse((mu_np[0], mu_np[1]), width=2*std[0], height=2*std[1], 
                             fill=False, edgecolor='red', linewidth=2, linestyle='--', 
                             label='1σ 范围', alpha=0.8)
    ellipse_2sigma = Ellipse((mu_np[0], mu_np[1]), width=4*std[0], height=4*std[1], 
                             fill=False, edgecolor='orange', linewidth=1.5, linestyle=':', 
                             label='2σ 范围', alpha=0.6)
    axes[0].add_patch(ellipse_1sigma)
    axes[0].add_patch(ellipse_2sigma)
    
    axes[0].set_xlabel('z₁ (第一潜在维度)', fontsize=13, fontweight='bold')
    axes[0].set_ylabel('z₂ (第二潜在维度)', fontsize=13, fontweight='bold')
    axes[0].set_title('VAE 潜在空间分布', fontsize=15, fontweight='bold', pad=15)
    axes[0].legend(loc='upper right', fontsize=11, framealpha=0.9)
    axes[0].grid(True, alpha=0.3, linestyle='--')
    axes[0].axhline(y=0, color='k', linewidth=0.8, alpha=0.3)
    axes[0].axvline(x=0, color='k', linewidth=0.8, alpha=0.3)
    
    # 添加统计信息
    info_text = f'μ = [{mu_np[0]:.3f}, {mu_np[1]:.3f}]\nσ = [{std[0]:.3f}, {std[1]:.3f}]'
    axes[0].text(0.02, 0.98, info_text, transform=axes[0].transAxes,
                fontsize=10, verticalalignment='top',
                bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
    
    # 右图:与标准正态分布对比
    standard_samples = np.random.randn(num_samples, L_LATENT)
    
    axes[1].scatter(standard_samples[:, 0], standard_samples[:, 1], alpha=0.3, s=30, 
                   c='lightgreen', edgecolors='green', linewidth=0.5, label='标准正态 N(0,I)')
    axes[1].scatter(samples[:, 0], samples[:, 1], alpha=0.4, s=40, 
                   c='lightblue', edgecolors='blue', linewidth=0.5, label='VAE 潜在分布')
    axes[1].scatter(mu_np[0], mu_np[1], c='red', s=300, marker='*', 
                   edgecolors='darkred', linewidths=2, label='VAE 均值 μ', zorder=10)
    axes[1].scatter(0, 0, c='green', s=300, marker='*', 
                   edgecolors='darkgreen', linewidths=2, label='标准正态均值', zorder=10)
    
    axes[1].set_xlabel('z₁ (第一潜在维度)', fontsize=13, fontweight='bold')
    axes[1].set_ylabel('z₂ (第二潜在维度)', fontsize=13, fontweight='bold')
    axes[1].set_title('潜在分布 vs 标准正态分布', fontsize=15, fontweight='bold', pad=15)
    axes[1].legend(loc='upper right', fontsize=11, framealpha=0.9)
    axes[1].grid(True, alpha=0.3, linestyle='--')
    axes[1].axhline(y=0, color='k', linewidth=0.8, alpha=0.3)
    axes[1].axvline(x=0, color='k', linewidth=0.8, alpha=0.3)
    
    # 添加说明
    explanation = 'KL散度损失使VAE的潜在分布逐渐接近标准正态分布N(0,I)这样可以确保潜在空间的连续性和可插值性'
    axes[1].text(0.5, -0.15, explanation, transform=axes[1].transAxes,
                fontsize=10, ha='center', style='italic',
                bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.7))
    
    plt.suptitle('VAE 潜在空间可视化 - 理解编码器的输出分布', 
                fontsize=17, fontweight='bold', y=0.98)
    plt.tight_layout()
    return fig
# --- 主程序 ---
if __name__ == "__main__":
    print("=" * 60)
    print("VAE 潜在空间可视化")
    print("=" * 60)
    
    # 实例化模型
    model = VAE(D_INPUT, H_HIDDEN, L_LATENT)
    
    # 前向传播
    with torch.no_grad():
        recon_batch, mu, logvar, z_latent = model(X_input)
        L_rec, L_kld, Total_Loss = vae_loss_function(recon_batch, X_input, mu, logvar)
    
    # 打印结果
    print(f"\n输入 X (4D): {X_input.squeeze().numpy().round(3)}")
    print(f"均值 μ (2D): {mu.squeeze().numpy().round(3)}")
    print(f"Log方差 log(σ²) (2D): {logvar.squeeze().numpy().round(3)}")
    print(f"标准差 σ (2D): {torch.exp(0.5 * logvar).squeeze().numpy().round(3)}")
    print(f"采样的潜在编码 z (2D): {z_latent.squeeze().numpy().round(3)}")
    print(f"重建 X̂ (4D): {recon_batch.squeeze().numpy().round(3)}")
    print("-" * 60)
    print(f"重构损失 (MSE): {L_rec.item():.4f}")
    print(f"KL 散度损失: {L_kld.item():.4f}")
    print(f"总损失 (ELBO): {Total_Loss.item():.4f}")
    print("=" * 60)
    
    # 创建潜在空间可视化
    print("\n正在生成潜在空间可视化...")
    fig = visualize_latent_space(mu, logvar, z_latent, num_samples=200)
    plt.savefig('vae_latent_space.png', dpi=150, bbox_inches='tight')
    print("✓ 可视化图表已保存为 'vae_latent_space.png'")
    
    plt.show()
    print("\n可视化完成!")

image.gif

image.gif 编辑

image.gif 编辑


目录
相关文章
|
15小时前
|
机器学习/深度学习 数据可视化 机器人
PyTorch深度学习实战 |手算​​自编码Autoencoder
自编码器是一种无监督神经网络,通过编码器将数据压缩为低维潜在表示,再由解码器重建原始输入。其核心价值在于自动提取关键特征、实现降维与数据去噪,广泛应用于图像重建、特征学习和可视化分析等领域。
31 1
|
14小时前
|
人工智能 安全 前端开发
AI应用软件的开发流程
AI应用开发以大模型为核心,区别于传统软件:强调数据调优、算法迭代与安全边界控制。全流程分六阶段——需求定义、技术选型、提示工程与知识库构建、前后端联调、AI专项评测(准确率/安全性/高并发)、灰度发布与持续进化。重在“人机协同”而非纯代码实现。(238字)
|
15小时前
|
人工智能 自然语言处理 供应链
智能体式邮件安全:面向源头阻断的钓鱼攻击主动防御体系研究
Doppel公司2026年5月推出Agentic Email Security,首创“智能体+威胁图谱”架构,突破传统邮件安全滞后性。系统实现上下文感知、攻击链溯源与多渠道源头关停,将防御重心从“单邮件阻断”升维至“攻击活动摧毁”,显著提升AI钓鱼对抗能力。(239字)
30 0
|
14小时前
|
人工智能 开发框架 自然语言处理
AI智能体的开发与上线
本文系统梳理AI智能体从构想到上线的六阶段非线性工程:需求界定、技术选型、能力组装、效果评测、灰度发布、持续迭代。覆盖提示词设计、知识库挂载、插件集成、安全测试与闭环优化,助力高效落地合规智能体。(239字)
|
15小时前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch深度学习实战 | 手算生成对抗网络GAN
GAN(生成对抗网络)是一种深度学习模型,由生成器与判别器构成对抗训练框架:生成器学习伪造逼真数据,判别器则努力区分真假。二者博弈迭代,最终生成器可产出以假乱真的高质量样本,广泛应用于图像生成、数据增强等领域。
27 0
|
15小时前
|
机器学习/深度学习 自然语言处理 PyTorch
PyTorch深度学习实战 |手算ViT(Vision Transformer)模型
ViT将图像分块为Patch,经卷积嵌入成Token序列,加入CLS Token和位置编码后输入Transformer Encoder。其核心是让简单分类头依赖Encoder提炼的强特征,凸显Transformer的全局特征提取能力,奠定多模态大模型基础。(239字)
35 0
|
15小时前
|
机器学习/深度学习 存储 算法
图解强化学习 |手算Sarsa算法
SARSA是一种基于价值的在线无模型强化学习算法,通过Q表存储状态-动作价值,采用ε-贪心策略与时序差分更新(TD),始终依据真实执行动作而非最优动作进行学习。其训练保守稳定、安全性高,但探索性较弱,且在大状态动作空间下易出现Q表爆炸问题。(239字)
30 0
|
15小时前
|
机器学习/深度学习 存储 编解码
PyTorch深度学习实战 | 手算卷积网络(Resnet-18)
ResNet-18是解决深层网络梯度消失与退化问题的经典模型,核心在于残差连接(Shortcut):让输入X直接跳跃传递,与卷积学习的残差F(X)相加(F(X)+X),实现恒等映射。其含4个stage、18层可训练层,每个BasicBlock由两个3×3卷积+BN+ReLU构成,并通过1×1卷积适配尺寸/通道差异,显著提升深层网络训练稳定性与性能。(239字)
27 0
|
12小时前
|
存储 Java
java工具:《list根据ids数组 过滤list》
java工具:《list根据ids数组 过滤list》
25 1
|
9小时前
|
人工智能 定位技术 知识图谱
lat.md:将任意项目代码转换为可查询的知识图谱
`lat.md` 是一款面向开发者的智能文档工具:它将代码与笔记双向关联,自动生成可校验的项目知识地图。支持20+语言、本地扫描、摘要优先、断链预警及保存时自动检查,确保文档始终与代码同步,让AI真正理解项目全貌。
23 0
lat.md:将任意项目代码转换为可查询的知识图谱