6.5_rnn-pytorch

本节介绍了如何使用PyTorch简洁地实现循环神经网络(RNN)进行语言建模。首先加载周杰伦歌词数据集,然后定义一个包含单隐藏层、256个隐藏单元的RNN层。在前向传播中,RNN返回每个时间步的隐藏状态和最后时间步的隐藏状态。接着定义了一个完整的RNN模型,包括RNN层和全连接输出层。训练模型时,采用了相邻采样读取数据,并用Adam优化器进行优化。在训练过程中,每50个周期输出一次预测结果,展示模型的预测能力。
部署运行你感兴趣的模型镜像

6.5 循环神经网络的简洁实现

本节将使用PyTorch来更简洁地实现基于循环神经网络的语言模型。首先,我们读取周杰伦专辑歌词数据集。

import time
import math
import numpy as np
import torch
from torch import nn, optim
import torch.nn.functional as F

import sys
sys.path.append("..") 
import d2lzh_pytorch as d2l
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

(corpus_indices, char_to_idx, idx_to_char, vocab_size) = d2l.load_data_jay_lyrics()

6.5.1 定义模型

PyTorch中的nn模块提供了循环神经网络的实现。下面构造一个含单隐藏层、隐藏单元个数为256的循环神经网络层rnn_layer

num_hiddens = 256
# rnn_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens) # 已测试
rnn_layer = nn.RNN(input_size=vocab_size, hidden_size=num_hiddens)

与上一节中实现的循环神经网络不同,这里rnn_layer的输入形状为(时间步数, 批量大小, 输入个数)。其中输入个数即one-hot向量长度(词典大小)。此外,rnn_layer作为nn.RNN实例,在前向计算后会分别返回输出和隐藏状态h,其中输出指的是隐藏层在各个时间步上计算并输出的隐藏状态,它们通常作为后续输出层的输入。需要强调的是,该“输出”本身并不涉及输出层计算,形状为(时间步数, 批量大小, 隐藏单元个数)。而nn.RNN实例在前向计算返回的隐藏状态指的是隐藏层在最后时间步的隐藏状态:当隐藏层有多层时,每一层的隐藏状态都会记录在该变量中;对于像长短期记忆(LSTM),隐藏状态是一个元组(h, c),即hidden state和cell state。我们会在本章的后面介绍长短期记忆和深度循环神经网络。关于循环神经网络(以LSTM为例)的输出,可以参考下图(图片来源)。

在这里插入图片描述

循环神经网络(以LSTM为例)的输出

来看看我们的例子,输出形状为(时间步数, 批量大小, 隐藏单元个数),隐藏状态h的形状为(层数, 批量大小, 隐藏单元个数)。

num_steps = 35
batch_size = 2
state = None
X = torch.rand(num_steps, batch_size, vocab_size)
Y, state_new = rnn_layer(X, state)
print(Y.shape, len(state_new), state_new[0].shape)

输出:

torch.Size([35, 2, 256]) 1 torch.Size([2, 256])

如果rnn_layernn.LSTM实例,那么上面的输出是什么?

接下来我们继承Module类来定义一个完整的循环神经网络。它首先将输入数据使用one-hot向量表示后输入到rnn_layer中,然后使用全连接输出层得到输出。输出个数等于词典大小vocab_size

# 本类已保存在d2lzh_pytorch包中方便以后使用
class RNNModel(nn.Module):
    def __init__(self, rnn_layer, vocab_size):
        super(RNNModel, self).__init__()
        self.rnn = rnn_layer
        self.hidden_size = rnn_layer.hidden_size * (2 if rnn_layer.bidirectional else 1) 
        self.vocab_size = vocab_size
        self.dense = nn.Linear(self.hidden_size, vocab_size)
        self.state = None

    def forward(self, inputs, state): # inputs: (batch, seq_len)
        # 获取one-hot向量表示
        X = d2l.to_onehot(inputs, self.vocab_size) # X是个list
        Y, self.state = self.rnn(torch.stack(X), state)
        # 全连接层会首先将Y的形状变成(num_steps * batch_size, num_hiddens),它的输出
        # 形状为(num_steps * batch_size, vocab_size)
        output = self.dense(Y.view(-1, Y.shape[-1]))
        return output, self.state

6.5.2 训练模型

同上一节一样,下面定义一个预测函数。这里的实现区别在于前向计算和初始化隐藏状态的函数接口。

# 本函数已保存在d2lzh_pytorch包中方便以后使用
def predict_rnn_pytorch(prefix, num_chars, model, vocab_size, device, idx_to_char,
                      char_to_idx):
    state = None
    output = [char_to_idx[prefix[0]]] # output会记录prefix加上输出
    for t in range(num_chars + len(prefix) - 1):
        X = torch.tensor([output[-1]], device=device).view(1, 1)
        if state is not None:
            if isinstance(state, tuple): # LSTM, state:(h, c)  
                state = (state[0].to(device), state[1].to(device))
            else:   
                state = state.to(device)
            
        (Y, state) = model(X, state)
        if t < len(prefix) - 1:
            output.append(char_to_idx[prefix[t + 1]])
        else:
            output.append(int(Y.argmax(dim=1).item()))
    return ''.join([idx_to_char[i] for i in output])

让我们使用权重为随机值的模型来预测一次。

model = RNNModel(rnn_layer, vocab_size).to(device)
predict_rnn_pytorch('分开', 10, model, vocab_size, device, idx_to_char, char_to_idx)

输出:

'分开戏想暖迎凉想征凉征征'

接下来实现训练函数。算法同上一节的一样,但这里只使用了相邻采样来读取数据。

# 本函数已保存在d2lzh_pytorch包中方便以后使用
def train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                                corpus_indices, idx_to_char, char_to_idx,
                                num_epochs, num_steps, lr, clipping_theta,
                                batch_size, pred_period, pred_len, prefixes):
    loss = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=lr)
    model.to(device)
    state = None
    for epoch in range(num_epochs):
        l_sum, n, start = 0.0, 0, time.time()
        data_iter = d2l.data_iter_consecutive(corpus_indices, batch_size, num_steps, device) # 相邻采样
        for X, Y in data_iter:
            if state is not None:
                # 使用detach函数从计算图分离隐藏状态, 这是为了
                # 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)
                if isinstance (state, tuple): # LSTM, state:(h, c)  
                    state = (state[0].detach(), state[1].detach())
                else:   
                    state = state.detach()
    
            (output, state) = model(X, state) # output: 形状为(num_steps * batch_size, vocab_size)
            
            # Y的形状是(batch_size, num_steps),转置后再变成长度为
            # batch * num_steps 的向量,这样跟输出的行一一对应
            y = torch.transpose(Y, 0, 1).contiguous().view(-1)
            l = loss(output, y.long())
            
            optimizer.zero_grad()
            l.backward()
            # 梯度裁剪
            d2l.grad_clipping(model.parameters(), clipping_theta, device)
            optimizer.step()
            l_sum += l.item() * y.shape[0]
            n += y.shape[0]
        
        try:
            perplexity = math.exp(l_sum / n)
        except OverflowError:
            perplexity = float('inf')
        if (epoch + 1) % pred_period == 0:
            print('epoch %d, perplexity %f, time %.2f sec' % (
                epoch + 1, perplexity, time.time() - start))
            for prefix in prefixes:
                print(' -', predict_rnn_pytorch(
                    prefix, pred_len, model, vocab_size, device, idx_to_char,
                    char_to_idx))

使用和上一节实验中一样的超参数(除了学习率)来训练模型。

num_epochs, batch_size, lr, clipping_theta = 250, 32, 1e-3, 1e-2 # 注意这里的学习率设置
pred_period, pred_len, prefixes = 50, 50, ['分开', '不分开']
train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,
                            corpus_indices, idx_to_char, char_to_idx,
                            num_epochs, num_steps, lr, clipping_theta,
                            batch_size, pred_period, pred_len, prefixes)

输出:

epoch 50, perplexity 10.658418, time 0.05 sec
 - 分开始我妈  想要你 我不多 让我心到的 我妈妈 我不能再想 我不多再想 我不要再想 我不多再想 我不要
 - 不分开 我想要你不你 我 你不要 让我心到的 我妈人 可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我疯狂的
epoch 100, perplexity 1.308539, time 0.05 sec
 - 分开不会痛 不要 你在黑色幽默 开始了美丽全脸的梦滴 闪烁成回忆 伤人的美丽 你的完美主义 太彻底 让我
 - 不分开不是我不要再想你 我不能这样牵着你的手不放开 爱可不可以简简单单没有伤害 你 靠着我的肩膀 你 在我
epoch 150, perplexity 1.070370, time 0.05 sec
 - 分开不能去河南嵩山 学少林跟武当 快使用双截棍 哼哼哈兮 快使用双截棍 哼哼哈兮 习武之人切记 仁者无敌
 - 不分开 在我会想通 是谁开没有全有开始 他心今天 一切人看 我 一口令秋软语的姑娘缓缓走过外滩 消失的 旧
epoch 200, perplexity 1.034663, time 0.05 sec
 - 分开不能去吗周杰伦 才离 没要你在一场悲剧 我的完美主义 太彻底 分手的话像语言暴力 我已无能为力再提起
 - 不分开 让我面到你 爱情来的太快就像龙卷风 离不开暴风圈来不及逃 我不能再想 我不能再想 我不 我不 我不
epoch 250, perplexity 1.021437, time 0.05 sec
 - 分开 我我外的家边 你知道这 我爱不看的太  我想一个又重来不以 迷已文一只剩下回忆 让我叫带你 你你的
 - 不分开 我我想想和 是你听没不  我不能不想  不知不觉 你已经离开我 不知不觉 我跟了这节奏 后知后觉 

小结

  • PyTorch的nn模块提供了循环神经网络层的实现。
  • PyTorch的nn.RNN实例在前向计算后会分别返回输出和隐藏状态。该前向计算并不涉及输出层计算。

注:除代码外本节与原书此节基本相同,原书传送门

您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

<think> 我们注意到在自动定位CUDNN时,得到的路径是`E:\Program Files\NVIDIA\CUNND\v9.12\include`,但是脚本在拼接路径时却错误地加上了两个`include`,导致路径变成了`E:\Program Files\NVIDIA\CUNND\v9.12\include\include\cudnn.h`,这显然是不正确的。 问题出在`verify_paths.ps1`脚本中,当我们自动定位到cudnn.h文件时,我们设置的环境变量`CUDNN_HOME`是cudnn.h所在目录的父目录(即`include`的上一级),但是在生成兼容层时,我们又在其后添加了`include`,导致路径重复。 修正方案: 1. 在`verify_paths.ps1`中,当我们自动定位到cudnn.h时,应该将`CUDNN_HOME`设置为cudnn.h所在目录的父目录(即`include`目录的上一级),这样在生成兼容层时,我们拼接的路径才是正确的。 2. 在`generate_compat.py`中,我们拼接路径时应该使用`os.path.join(os.environ['CUDNN_HOME'], 'include', 'cudnn.h')`,这样如果`CUDNN_HOME`设置正确,就能找到cudnn.h。 但是,从错误信息看,自动定位到的路径已经是`include`目录了(因为cudnn.h在`include`目录下),所以设置`CUDNN_HOME`为`include`目录的上一级。例如,如果cudnn.h的完整路径是`E:\Program Files\NVIDIA\CUNND\v9.12\include\cudnn.h`,那么`CUDNN_HOME`应该设置为`E:\Program Files\NVIDIA\CUNND\v9.12`。 因此,我们需要修改`verify_paths.ps1`中自动定位后的设置: 原代码: ```powershell if ($found) { $env:CUDNN_HOME = Split-Path (Split-Path $found) ... } ``` 这里`Split-Path $found`会得到`E:\Program Files\NVIDIA\CUNND\v9.12\include`,再Split一次就得到了`E:\Program Files\NVIDIA\CUNND\v9.12`。所以这里设置是正确的。 那么问题可能在于自动定位到的文件路径`$found`是`E:\Program Files\NVIDIA\CUNND\v9.12\include\cudnn.h`,那么: Split-Path $found -> 得到目录`E:\Program Files\NVIDIA\CUNND\v9.12\include` Split-Path (Split-Path $found) -> 得到`E:\Program Files\NVIDIA\CUNND\v9.12` 然后在`generate_compat.py`中,我们拼接为`os.path.join(os.environ['CUDNN_HOME'], 'include', 'cudnn.h')`,即`E:\Program Files\NVIDIA\CUNND\v9.12\include\cudnn.h`,这是正确的。 但是错误信息是: 错误:找不到 E:\Program Files\NVIDIA\CUNND\v9.12\include\include\cudnn.h 这说明在`generate_compat.py`中,我们使用的路径被重复添加了`include`。这可能是由于我们自动定位时,`$env:CUDNN_HOME`被设置成了`E:\Program Files\NVIDIA\CUNND\v9.12\include`(即多了一层include),而我们在代码中又添加了`include`,导致变成`include\include`。 所以,我们需要确保自动定位时,`$env:CUDNN_HOME`被设置为包含`include`目录的路径,而不是`include`目录本身。 修改`verify_paths.ps1`:当自动定位到cudnn.h时,设置`$env:CUDNN_HOME`为包含`include`目录的路径(即`include`的父目录)。 另外,在自动搜索时,我们得到的`$found`是cudnn.h的完整路径,那么: 包含`include`目录的路径 = Split-Path (Split-Path $found) # 即去掉最后的文件名和include目录 因此,我们不需要修改,但需要检查实际定位到的路径。 为了确保安全,我们在`verify_paths.ps1`中输出实际设置的`CUDNN_HOME`和拼接后的路径,以便调试。 修正后的`verify_paths.ps1`(增加调试信息): ```powershell $cudnnPath = "E:\NVIDIA\CUDNN\v9.12\include\cudnn.h" if (-not (Test-Path $cudnnPath)) { # 自动搜索cudnn.h $found = Get-ChildItem -Path "E:\" -Recurse -Filter "cudnn.h" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty FullName if ($found) { $parentDir = Split-Path $found # 包含cudnn.h的目录(即include) $env:CUDNN_HOME = Split-Path $parentDir # 去掉include,得到CUDNN的根目录 Write-Host "自动定位到CUDNN: $env:CUDNN_HOME" -ForegroundColor Green } else { Write-Host "错误:找不到cudnn.h,请检查CUDNN安装位置" -ForegroundColor Red exit 1 } } else { $env:CUDNN_HOME = "E:\NVIDIA\CUDNN\v9.12" } # 验证结果 $testPath = Join-Path $env:CUDNN_HOME "include\cudnn.h" Write-Host "测试路径: $testPath" if (-not (Test-Path $testPath)) { Write-Host "错误:在$testPath找不到cudnn.h" -ForegroundColor Red exit 1 } else { Write-Host "cudnn.h验证成功" -ForegroundColor Green } Write-Host "CUDA_HOME: $env:CUDA_HOME" Write-Host "CUDNN_HOME: $env:CUDNN_HOME" ``` 另外,在`generate_compat.py`中,我们也添加路径验证: ```python import os import sys def generate_adapter(): # 确保CUDNN_HOME存在 if 'CUDNN_HOME' not in os.environ: print("错误:环境变量CUDNN_HOME未设置") sys.exit(1) # 拼接cudnn.h路径 cudnn_h = os.path.join(os.environ['CUDNN_HOME'], 'include', 'cudnn.h') print(f"尝试访问: {cudnn_h}") if not os.path.exists(cudnn_h): print(f"错误:找不到 {cudnn_h}") sys.exit(1) adapter_code = r""" #include <cudnn.h> // 简化版适配器 - 仅处理最常见的兼容性问题 cudnnStatus_t cudnnRNNBackwardWeights_v8( cudnnHandle_t handle, const cudnnRNNDescriptor_t rnnDesc, const int seqLength, const cudnnTensorDescriptor_t* xDesc, const void* x, const cudnnTensorDescriptor_t hxDesc, const void* hx, const cudnnTensorDescriptor_t* yDesc, const void* y, const cudnnFilterDescriptor_t dwDesc, void* dw, const void* workspace, size_t workSpaceSizeInBytes, const void* reserveSpace, size_t reserveSpaceSizeInBytes) { // 实际调用新版函数 return cudnnRNNBackwardWeights( handle, rnnDesc, seqLength, xDesc, x, hxDesc, hx, yDesc, y, workspace, workSpaceSizeInBytes, dwDesc, dw, reserveSpace, reserveSpaceSizeInBytes); } // 添加其他需要适配的函数... """ os.makedirs("third_party/cuda_compat", exist_ok=True) out_file = "third_party/cuda_compat/cudnn_adapter.cu" with open(out_file, "w") as f: f.write(adapter_code) print(f"兼容层已生成到: {out_file}") if __name__ == "__main__": generate_adapter() ``` 同时,我们修改`build_simple.ps1`,在生成兼容层之前先执行路径验证脚本,并确保路径正确。 此外,我们注意到在自动定位时,目录名可能是大小写敏感的?比如,实际路径可能是`CuDNN`而不是`CUNND`?所以,在自动搜索时,我们可能需要忽略大小写。 修改自动搜索部分(添加`-Force`和`-ErrorAction`以确保访问所有目录): ```powershell $found = Get-ChildItem -Path "E:\" -Recurse -Filter "cudnn.h" -ErrorAction SilentlyContinue -Force | Select-Object -First 1 -ExpandProperty FullName ``` 最后,重新运行构建脚本。 如果问题仍然存在,请检查`$env:CUDNN_HOME`是否正确,以及生成的路径是否确实存在。 让我们重新整理一下,提供完整的修正后脚本。 由于您已经创建了虚拟环境并激活,接下来只需重新运行构建脚本,我们会确保路径正确。 如果自动定位仍然失败,我们还可以手动设置环境变量: ```powershell $env:CUDNN_HOME = "您安装CUDNN的真实路径(例如:E:\Program Files\NVIDIA\CuDNN\v9.12)" ``` 请确保这个路径下有一个`include`目录,而`include`目录下有一个`cudnn.h`文件。 现在,我们提供完整的修正脚本: ### verify_paths.ps1 ```powershell $cudnnPath = "E:\NVIDIA\CUDNN\v9.12\include\cudnn.h" if (-not (Test-Path $cudnnPath)) { # 自动搜索cudnn.h,忽略大小写,强制搜索所有目录 Write-Host "正在搜索cudnn.h,可能需要一段时间..." $found = Get-ChildItem -Path "E:\" -Recurse -Force -ErrorAction SilentlyContinue -Filter "cudnn.h" | Select-Object -First 1 -ExpandProperty FullName if ($found) { $parentDir = Split-Path $found # 包含cudnn.h的目录(即include) $env:CUDNN_HOME = Split-Path $parentDir # 去掉include,得到CUDNN的根目录 Write-Host "自动定位到CUDNN: $env:CUDNN_HOME" -ForegroundColor Green } else { Write-Host "错误:找不到cudnn.h,请检查CUDNN安装位置" -ForegroundColor Red exit 1 } } else { $env:CUDNN_HOME = "E:\NVIDIA\CUDNN\v9.12" } # 验证结果 $testPath = Join-Path $env:CUDNN_HOME "include\cudnn.h" Write-Host "测试路径: $testPath" if (-not (Test-Path $testPath)) { Write-Host "错误:在$testPath找不到cudnn.h" -ForegroundColor Red exit 1 } else { Write-Host "cudnn.h验证成功" -ForegroundColor Green } Write-Host "CUDA_HOME: $env:CUDA_HOME" Write-Host "CUDNN_HOME: $env:CUDNN_HOME" ``` ### generate_compat.py 保持不变(使用上面修正后的代码)。 ### build_simple.ps1 保持不变。 然后重新运行。 如果还有问题,请手动设置`CUDNN_HOME`环境变量,并注释掉自动搜索部分。 希望这次能成功! §§相关问题§§: 1. 如何在PowerShell中递归搜索文件并处理可能的权限问题? 2. 当自动环境配置失败时,如何设计回退机制让用户手动输入路径? 3. 有哪些有效的方法可以调试Python与PowerShell混合脚本? 4. 在编译大型C++项目时,如何快速定位头文件缺失问题? </think> 看到问题了!在路径处理上犯了错误。脚本将`CUDNN_HOME`设置成了包含文件的目录而不是根目录。让我修复这个问题并提供更健壯的解决方案。 ### 修正后的完整解决方案 #### 1. 智能路径查找脚本 (`find_cudnn.ps1`) ```powershell # 保存为 find_cudnn.ps1 param() function Find-Cudnn { $possiblePaths = @( "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDNN", "C:\Program Files\NVIDIA\CUDNN", "E:\NVIDIA\CUDNN", "C:\cudnn", "${env:ProgramFiles}\NVIDIA\CUDNN" ) # 添加环境变量路径 if ($env:CUDNN_HOME) { $possiblePaths += $env:CUDNN_HOME } # 搜索可能的路径 foreach ($path in $possiblePaths) { if (-not (Test-Path $path)) { continue } $includePath = Join-Path $path "include\cudnn.h" if (Test-Path $includePath) { Write-Host "找到CUDNN:$path" -ForegroundColor Green return $path } # 尝试搜索子目录 $subdirs = Get-ChildItem -Path $path -Directory -Filter "v*" | Sort-Object Name -Descending foreach ($dir in $subdirs) { $includePath = Join-Path $dir.FullName "include\cudnn.h" if (Test-Path $includePath) { Write-Host "找到CUDNN:$($dir.FullName)" -ForegroundColor Green return $dir.FullName } } } # 全盘搜索作为最后手段 Write-Host "正在全盘搜索cudnn.h,这可能需要几分钟..." -ForegroundColor Yellow $found = Get-ChildItem -Path "C:\","E:\" -Recurse -Filter "cudnn.h" -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty DirectoryName if ($found) { $cudnnRoot = $found | Split-Path Write-Host "自动定位到CUDNN: $cudnnRoot" -ForegroundColor Green return $cudnnRoot } Write-Host "错误:找不到cudnn.h,请手动设置CUDNN_HOME环境变量" -ForegroundColor Red exit 1 } # 主逻辑 $cudnnRoot = Find-Cudnn if (-not $cudnnRoot) { exit 1 } # 设置环境变量 $env:CUDNN_HOME = $cudnnRoot Write-Host "已设置 CUDNN_HOME = $env:CUDNN_HOME" -ForegroundColor Cyan ``` #### 2. 简化版兼容层生成器 (`generate_compat.py`) ```python import os import sys import logging # 配置日志 logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') def generate_adapter(): # 检查环境变量 required_envs = ['CUDNN_HOME', 'CUDA_HOME'] missing_envs = [env for env in required_envs if env not in os.environ] if missing_envs: logging.error(f"缺少必需的环境变量: {', '.join(missing_envs)}") sys.exit(1) # 获取路径 cudnn_include = os.path.join(os.environ['CUDNN_HOME'], 'include') cudnn_h = os.path.join(cudnn_include, 'cudnn.h') # 验证路径 if not os.path.exists(cudnn_include): logging.error(f"目录不存在: {cudnn_include}") sys.exit(1) if not os.path.exists(cudnn_h): logging.error(f"文件不存在: {cudnn_h}") logging.info("请检查CUDNN安装目录是否包含include/cudnn.h") sys.exit(1) logging.info(f"使用CUDNN头文件: {cudnn_h}") # 适配器代码模板 adapter_code = f"""/* * CUDNN 兼容适配器 * 生成时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} * CUDNN路径: {os.environ['CUDNN_HOME']} */ #include <cudnn.h> // API版本适配宏 #if CUDNN_MAJOR >= 9 #define CUDNN_COMPAT_MODE #endif // RNN函数适配 #ifdef CUDNN_COMPAT_MODE cudnnStatus_t cudnnRNNBackwardWeights_v8( cudnnHandle_t handle, const cudnnRNNDescriptor_t rnnDesc, const int seqLength, const cudnnTensorDescriptor_t* xDesc, const void* x, const cudnnTensorDescriptor_t hxDesc, const void* hx, const cudnnTensorDescriptor_t* yDesc, const void* y, const cudnnFilterDescriptor_t dwDesc, void* dw, const void* workspace, size_t workSpaceSizeInBytes, const void* reserveSpace, size_t reserveSpaceSizeInBytes) {{ return cudnnRNNBackwardWeights( handle, rnnDesc, seqLength, xDesc, x, hxDesc, hx, yDesc, y, workspace, workSpaceSizeInBytes, dwDesc, dw, reserveSpace, reserveSpaceSizeInBytes); }} #endif // 添加更多适配函数... """ # 创建兼容层目录 compat_dir = "third_party/cuda_compat" os.makedirs(compat_dir, exist_ok=True) # 写入文件 output_file = os.path.join(compat_dir, "cudnn_adapter.cu") with open(output_file, "w") as f: f.write(adapter_code) logging.info(f"兼容层已生成: {output_file}") return True if __name__ == "__main__": from datetime import datetime generate_adapter() ``` #### 3. 增强版构建脚本 (`build_enhanced.ps1`) ```powershell # 保存为 build_enhanced.ps1 # 0. 初始化 $ErrorActionPreference = "Stop" Set-StrictMode -Version 3.0 $startTime = Get-Date Write-Host "开始 PyTorch 自定义构建 - $(Get-Date)" -ForegroundColor Cyan # 1. 查找 CUDA/CUDNN try { Write-Host "`n步骤1: 定位CUDA和CUDNN" -ForegroundColor Yellow . .\find_cudnn.ps1 # 确保 CUDA_HOME 已设置 if (-not $env:CUDA_HOME) { $env:CUDA_HOME = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.1" Write-Host "使用默认CUDA路径: $env:CUDA_HOME" -ForegroundColor Yellow } Write-Host "CUDA_HOME: $env:CUDA_HOME" Write-Host "CUDNN_HOME: $env:CUDNN_HOME" } catch { Write-Host "环境设置失败: $_" -ForegroundColor Red exit 1 } # 2. 生成兼容层 try { Write-Host "`n步骤2: 生成CUDNN兼容层" -ForegroundColor Yellow python generate_compat.py if (-not $?) { throw "生成兼容层失败" } } catch { Write-Host "生成兼容层错误: $_" -ForegroundColor Red exit 2 } # 3. 应用补丁 try { Write-Host "`n步骤3: 修改构建配置" -ForegroundColor Yellow # 修改Dependencies.cmake $depsFile = "cmake\Dependencies.cmake" $content = Get-Content $depsFile -Raw $patchedContent = $content -replace 'if\(NOT \(CUDNN_VERSION VERSION_GREATER_EQUAL 8\.5\.0\)\)', 'if(NOT (TRUE)) # 兼容性绕过' Set-Content $depsFile $patchedContent -NoNewline Write-Host "已绕过版本检查" -ForegroundColor Green # 修改Cuda.cmake添加兼容层 $cudaFile = "cmake\Cuda.cmake" $cudaContent = Get-Content $cudaFile -Raw $injectionPoint = 'list(APPEND Caffe2_CUDA_SRCS ${Caffe2_CUDA_SRCS})' $injectionCode = @" # 注入CUDNN兼容层 list(APPEND CMAKE_CUDA_INCLUDE_DIRECTORIES `$\{PROJECT_SOURCE_DIR}/third_party/cuda_compat) list(APPEND Caffe2_CUDA_SRCS `$\{PROJECT_SOURCE_DIR}/third_party/cuda_compat/cudnn_adapter.cu) "@ $patchedCudaContent = $cudaContent.Replace( $injectionPoint, "$injectionCode`n$injectionPoint" ) Set-Content $cudaFile $patchedCudaContent -NoNewline Write-Host "已注入兼容层" -ForegroundColor Green } catch { Write-Host "修改配置失败: $_" -ForegroundColor Red exit 3 } # 4. 开始编译 try { Write-Host "`n步骤4: 编译PyTorch (这将需要较长时间)" -ForegroundColor Yellow # 设置环境变量 $env:USE_CUDA = "1" $env:USE_CUDNN = "1" $env:MAX_JOBS = [System.Environment]::ProcessorCount # 启动编译 $logFile = "build_$(Get-Date -Format 'yyyyMMdd_HHmmss').log" Write-Host "编译日志: $logFile" -ForegroundColor Cyan & { python setup.py clean python setup.py install --cmake } *> $logFile if ($LASTEXITCODE -eq 0) { Write-Host "`n编译成功! 耗时: $((Get-Date) - $startTime)" -ForegroundColor Green } else { throw "编译失败 (代码: $LASTEXITCODE)" } } catch { Write-Host "编译错误: $_" -ForegroundColor Red Write-Host "请查看日志文件: $logFile" -ForegroundColor Yellow exit 4 } # 5. 验证 try { Write-Host "`n步骤5: 验证安装" -ForegroundColor Yellow python -c "import torch; print(f'PyTorch版本: {torch.__version__}'); print(f'CUDA可用: {torch.cuda.is_available()}'); print(f'cuDNN版本: {torch.backends.cudnn.version()}')" } catch { Write-Host "验证失败: $_" -ForegroundColor Red exit 5 } Write-Host "`n所有步骤完成! 耗时: $((Get-Date) - $startTime)" -ForegroundColor Green ``` ### 执行流程说明 ```powershell # 1. 进入项目目录 cd E:\PyTorch_Build\pytorch # 2. 激活环境 .\rtx5070_env\Scripts\Activate.ps1 # 3. 运行增强版构建脚本 .\build_enhanced.ps1 ``` ### 关键改进 1. **智能路径查找**: - 多层级搜索CUDNN安装路径 - 支持环境变量和默认位置 - 全盘搜索作为后备方案 2. **错误处理**: - 详细的错误捕获和报告 - 每个步骤独立错误处理 - 状态和时间跟踪 3. **自动化配置**: - 自动修改CMake配置 - 精确注入兼容层代码 - 绕过版本检查 4. **日志记录**: - 时间戳日志文件 - 详细的步骤信息 - 错误上下文记录 ### 验证脚本 (`verify_pytorch.py`) ```python import torch import platform print("="*50) print(f"PyTorch 安装验证") print("="*50) print(f"操作系统: {platform.system()} {platform.release()}") print(f"PyTorch版本: {torch.__version__}") print(f"CUDA可用: {torch.cuda.is_available()}") print(f"CUDA版本: {torch.version.cuda}") print(f"cuDNN版本: {torch.backends.cudnn.version()}") print(f"GPU设备: {torch.cuda.get_device_name(0)}") # 测试基本功能 print("\n运行简单计算测试...") a = torch.randn(1000, 1000).cuda() b = torch.randn(1000, 1000).cuda() c = a @ b print(f"矩阵乘法测试: 结果形状 {c.shape} | 均值={c.mean().item():.4f}") # 测试RNN功能 print("\n测试RNN功能...") try: rnn = torch.nn.RNN(128, 256).cuda() input = torch.randn(32, 10, 128).cuda() output, _ = rnn(input) print(f"RNN测试成功! 输出形状: {output.shape}") except Exception as e: print(f"RNN测试失败: {str(e)}") print("\n验证完成!") ``` 执行验证: ```powershell python verify_pytorch.py ``` 这个增强版解决方案应该能解决所有路径和环境问题,并提供详细的错误报告和日志记录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值