如何用PyTorch快速准确地建立神经网络-创新互联

本篇文章给大家分享的是有关如何用PyTorch快速准确地建立神经网络,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

10年的桐乡网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。营销型网站建设的优势是能够根据用户设备显示端的尺寸不同,自动调整桐乡建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“桐乡网站设计”,“桐乡网站推广”以来,每个客户项目都认真落实执行。

你可能已经在社交媒体上看到过N次关于PyTorch和 TensorFlow的两极分化的争论。这些框架的普及推动了近年来深度学习的兴起。二者都不乏坚定的支持者,但在过去的一年里,一个明显的赢家已经开始出现。

PyTorch是2018年最流行的框架之一。它已迅速成为学术界和工业界研究人员的选深度学习框架。在过去几周使用了PyTorch之后,我体会到它是一个非常灵活且易于使用的深度学习库。

我们将不止学习理论-还包括编写4个不同的用例,看看PyTorch的表现如何。建立深度学习模型从来没有这么有趣过!

什么是PyTorch?

在深入研究PyTorch的实现之前,让我们先了解一下PyTorch是什么,以及为什么它最近会变得如此流行。

PyTorch是一个基于Python的科学计算包,类似于NumPy,它具备GPU附加功能。与此同时,它也是一个深度学习框架,为实现和构建深层神经网络体系结构提供了大程度的灵活性和速度。

最近发布的PyTorch 1.0帮助研究人员应对以下四大挑战:

  • 大面积的返工

  • 耗时的训练

  • Python语言缺乏灵活性

  • 慢速扩展

从本质上讲,PyTorch与其他深度学习框架有两个不同点:

  • 命令式编程

  • 动态计算图

命令式编程:PyTorch在遍历每一行代码的同时执行计算,这与Python程序的执行方式非常类似,这一概念称为命令式编程,它的大优点是可以动态地调试代码和编程逻辑。

动态计算图:PyTorch被称为“由运行定义的”框架,这意味着计算图结构(神经网络体系结构)是在运行时生成的。该属性的主要优点是:它提供了一个灵活的编程运行时接口,通过连接操作来方便系统的构建和修改。在PyTorch中,每个前向通路处定义一个新的计算图,这与使用静态图的TensorFlow形成了鲜明的对比。

PyTorch2.0附带了一个名为torch.jit的重要特性,它是一个高级编译器,允许用户分离模型和代码。此外,它还支持在定制硬件(如GPU或TPU)上进行有效的模型优化。

用PyTorch构建神经网络

让我们通过一个实际案例来理解PyTorch。学习理论固然好,但是如果你不把它付诸实践的话,它就没有多大用处了!

神经网络的PyTorch实现看起来与NumPy实现完全一样。本节的目标是展示PyTorch和NumPy的等效性质。为此,让我们创建一个简单的三层网络,在输入层中有5个节点,在隐藏层中有3个节点,在输出层中有1个节点。我们只使用一个带有五个特征和一个目标的单行训练示例。

import torch
n_input, n_hidden, n_output = 5, 3, 1

第一步是进行参数初始化。这里,每个层的权重和偏置参数被初始化为张量变量。张量是PyTorch的基本数据结构,用于建立不同类型的神经网络。可以将它们当作是数组和矩阵的推广,换句话说,张量是N维矩阵。

## initialize tensor for inputs, and outputs
x = torch.randn((1, n_input))
y = torch.randn((1, n_output))
## initialize tensor variables for weights
w1 = torch.randn(n_input, n_hidden) # weight for hidden layer
w2 = torch.randn(n_hidden, n_output) # weight for output layer
## initialize tensor variables for bias terms
b1 = torch.randn((1, n_hidden)) # bias for hidden layer
b2 = torch.randn((1, n_output)) # bias for output layer

在参数初始化完成之后,可以通过以下四个关键步骤来定义和训练神经网络:

  • 前向传播

  • 损失计算

  • 反向传播

  • 更新参数

让我们更详细地了解每一个步骤。

前向传播:在这个步骤中,每个层都使用以下两个公式计算激活流。这些激活流从输入层流向输出层,以生成最终输出。

1. z = weight * input + bias
2. a = activation_function (z)

下面的代码块显示了如何用PyTorch编写这些步骤。请注意,大多数函数,如指数和矩阵乘法,均与NumPy中的函数相类似。

## sigmoid activation function using pytorch
def sigmoid_activationreturn 1 / (1 + torch.exp(-z))
## activation of hidden layer
z1 = torch.mm(x, w1) + b1
a1 = sigmoid_activation(z1)
## activation (output) of final layer
z2 = torch.mm(a1, w2) + b2
output = sigmoid_activation(z2)

损失计算:这一步在输出层中计算误差 (也称为损失)。一个简单的损失函数可以用来衡量实际值和预测值之间的差异。稍后,我们将查看PyTorch中可用的不同类型的损失函数。

loss = y - output

反向传播:这一步的目的是通过对偏差和权重进行边际变化,从而将输出层的误差降到最低,边际变化是利用误差项的导数计算出来的。

根据链规则的微积分原理,将增量变化返回到隐藏层,并对其权重和偏差进行相应的修正。通过对权重和偏差的调整,使得误差最小化。

## function to calculate the derivative of activation
def sigmoid_deltareturn x * (1 - x)
## compute derivative of error terms
delta_output = sigmoid_delta(output)
delta_hidden = sigmoid_delta(a1)
## backpass the changes to previous layers
d_outp = loss * delta_output
loss_h = torch.mm(d_outp, w2.t())
d_hidn = loss_h * delta_hidden

更新参数:最后一步,利用从上述反向传播中接收到的增量变化来对权重和偏差进行更新。

learning_rate = 0.1
w2 += torch.mm(a1.t(), d_outp) * learning_rate
w1 += torch.mm(x.t(), d_hidn) * learning_rate
b2 += d_outp.sum() * learning_rate
b1 += d_hidn.sum() * learning_rate

当使用大量训练示例对多个历元执行这些步骤时,损失将降至最小值。得到最终的权重和偏差值之后,用它对未知数据进行预测。

用例1:手写数字分类

在上一节中,我们看到了用PyTorch编写神经网络的简单用例。在本节中,我们将利用PyTorch提供的不同的实用程序包(nn、autograd、Optimm、torchvision、torchtext等)来建立和训练神经网络。

利用这些包可以方便地定义和管理神经网络。在这个用例中,我们将创建一个多层感知器(MLP)网络,用于构建手写数字分类器。我们将使用torchvision包中的MNIST数据集。

与你将要从事的任何项目一样,第一步是数据预处理:首先需要将原始数据集转换为张量,并在固定范围内将其归一化。torchvision包提供了一个名为 transforms的实用程序,利用它可以将不同的转换组合在一起。

from torchvision import transforms
_tasks = transforms.Compose([
   transforms.ToTensor(),
   transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
   ])

第一个转换是将原始数据转换为张量,第二个转换是通过以下操作执行归一化:

x_normalized = x-mean / std

数值为0.5,0.5表示红色、绿色和蓝色三个通道的均值和标准差。

from torchvision.datasets import MNIST
## Load MNIST Dataset and apply transformations
mnist = MNIST("data", download=True, train=True, transform=_tasks)

PyTorch的另一个出色的实用工具是DataLoader迭代器,它为多个处理器之间并行地批处理、搬移和加载数据提供了实现的可能。为了评估这个模型,我们将数据集划分为训练集和验证集。

from torch.utils.data import DataLoader
from torch.utils.data.sampler import SubsetRandomSampler
## create training and validation split
split = int(0.8 * len(mnist))
index_list = list(range(len(mnist)))
train_idx, valid_idx = index_list[:split], index_list[split:]
## create sampler objects using SubsetRandomSampler
tr_sampler = SubsetRandomSampler(train_idx)
val_sampler = SubsetRandomSampler(valid_idx)
## create iterator objects for train and valid datasets
trainloader = DataLoader(mnist, batch_size=256, sampler=tr_sampler)
validloader = DataLoader(mnist, batch_size=256, sampler=val_sampler)

PyTorch中的神经网络架构可以定义为一个类,这个类继承了称为Module的nn包的基础类的所有属性。来自nn.Module类的继承使得我们可以轻松地实现、访问和调用多个方法,还可以定义类的构造函数中的各个层,以及前向传播步骤中的前向函数。

我们将定义一个具有以下层配置的网络:[784,128,10]。此配置表示输入层中有784个节点(28*28像素)、隐藏层中有128个节点,输出层中有10个节点。在前向函数中,我们将在隐藏层(可以通过nn模块访问)中使用Sigmoid激活函数。

import torch.nn.functional as F
class Modeldef __init__784, 128)
       self.output = nn.Linear(128, 10)
  def forwardreturn x
model = Model()

利用nn和Optim包定义损失函数和优化器:

from torch import optim
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay= 1e-6, momentum = 0.9, nesterov = True)

现在已经准备好,可以开始训练模型了,其核心步骤与前一节相同:前向传播、损失计算、反向传播和更新参数。

for epoch in range(1, 11): ## run the model for 10 epochs
   train_loss, valid_loss = [], []
   ## training part
   model.train()
   for data, target in trainloader:
       optimizer.zero_grad()
       ## 1. forward propagation
       output = model(data)
       ## 2. loss calculation
       loss = loss_function(output, target)
       ## 3. backward propagation
       loss.backward()
       ## 4. weight optimization
       optimizer.step()
       train_loss.append(loss.item())
   ## evaluation part
   model.eval()
   for data, target in validloader:
       output = model(data)
       loss = loss_function(output, target)
       valid_loss.append(loss.item())
   print ("Epoch:", epoch, "Training Loss: ", np.mean(train_loss), "Valid Loss: ", np.mean(valid_loss))
>> Epoch: 1  Training Loss:  0.645777 Valid Loss:  0.344971
>> Epoch: 2  Training Loss:  0.320241 Valid Loss:  0.299313
>> Epoch: 3  Training Loss:  0.278429 Valid Loss:  0.269018
>> Epoch: 4  Training Loss:  0.246289 Valid Loss:  0.237785
>> Epoch: 5  Training Loss:  0.217010 Valid Loss:  0.217133
>> Epoch: 6  Training Loss:  0.193017 Valid Loss:  0.206074
>> Epoch: 7  Training Loss:  0.174385 Valid Loss:  0.180163
>> Epoch: 8  Training Loss:  0.157574 Valid Loss:  0.170064
>> Epoch: 9  Training Loss:  0.144316 Valid Loss:  0.162660
>> Epoch: 10 Training Loss:  0.133053 Valid Loss:  0.152957

完成了模型的训练之后,即可在验证数据基础上进行预测。

## dataloader for validation dataset
dataiter = iter(validloader)
data, labels = dataiter.next()
output = model(data)
_, preds_tensor = torch.max(output, 1)
preds = np.squeeze(preds_tensor.numpy())
print ("Actual:", labels[:10])
print ("Predicted:", preds[:10])
>>> Actual: [0 1 1 1 2 2 8 8 2 8]
>>> Predicted: [0 1 1 1 2 2 8 8 2 8]

用例2:物体图像分类

现在让我们更进一步。

在这个用例中,我们将在PyTorch中创建卷积神经网络(CNN)架构,利用流行的CIFAR-10数据集进行物体图像分类,此数据集也包含在torchvision包中。定义和训练模型的整个过程将与以前的用例相同,唯一的区别只是在网络中引入了额外的层。

加载并转换数据集:

## load the dataset
from torchvision.datasets import CIFAR10
cifar = CIFAR10('data', train=True, download=True, transform=_tasks)
## create training and validation split
split = int(0.8 * len(cifar))
index_list = list(range(len(cifar)))
train_idx, valid_idx = index_list[:split], index_list[split:]
## create training and validation sampler objects
tr_sampler = SubsetRandomSampler(train_idx)
val_sampler = SubsetRandomSampler(valid_idx)
## create iterator objects for train and valid datasets
trainloader = DataLoader(cifar, batch_size=256, sampler=tr_sampler)
validloader = DataLoader(cifar, batch_size=256, sampler=val_sampler)

我们将创建三个用于低层特征提取的卷积层、三个用于大信息量提取的池化层和两个用于线性分类的线性层。

class Modeldef __init__## define the layers
       self.conv1 = nn.Conv2d(3, 16, 3, padding=1)
       self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
       self.conv3 = nn.Conv2d(32, 64, 3, padding=1)
       self.pool = nn.MaxPool2d(2, 2)
       self.linear1 = nn.Linear(1024, 512)
       self.linear2 = nn.Linear(512, 10)
   def forward-1, 1024) ## reshaping
       x = F.relu(self.linear1(x))
       x = self.linear2(x)
       return x
model = Model()

定义损失函数和优化器:

import torch.optim as optim
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay= 1e-6, momentum = 0.9, nesterov = True)
## run for 30 Epochs
for epoch in range(1, 31):
   train_loss, valid_loss = [], []
   ## training part
   model.train()
   for data, target in trainloader:
       optimizer.zero_grad()
       output = model(data)
       loss = loss_function(output, target)
       loss.backward()
       optimizer.step()
       train_loss.append(loss.item())
   ## evaluation part
   model.eval()
   for data, target in validloader:
       output = model(data)
       loss = loss_function(output, target)
       valid_loss.append(loss.item())

完成了模型的训练之后,即可在验证数据基础上进行预测。

## dataloader for validation dataset
dataiter = iter(validloader)
data, labels = dataiter.next()
output = model(data)
_, preds_tensor = torch.max(output, 1)
preds = np.squeeze(preds_tensor.numpy())
print ("Actual:", labels[:10])
print ("Predicted:", preds[:10])
Actual: ['truck', 'truck', 'truck', 'horse', 'bird', 'truck', 'ship', 'bird', 'deer', 'bird']
Pred:   ['truck', 'automobile', 'automobile', 'horse', 'bird', 'airplane', 'ship', 'bird', 'deer', 'bird']

用例3:情感文本分类

我们将从计算机视觉用例转向自然语言处理,目的是展示PyTorch在不同领域的不同应用。

在本节中,我们将利用基于RNN(递归神经网络)和LSTM(长短期记忆)层的Pyotch来完成文本分类任务。首先,加载包含两个字段(文本和目标)的数据集。目标包含两个类:class1和class2,我们的任务是将每个文本分为其中一个类。

可以在下面的链接中下载数据集。

https://s3-ap-south-1.amazonaws.com/av-blog-media/wp-content/uploads/2019/01/train.csv

train = pd.read_csv("train.csv")
x_train = train["text"].values
y_train = train['target'].values

强烈建议在编码之前先设置种子,它可以保证你看到的结果与我的相同-这是在学习新概念时非常有用(也很有益)的特征。

np.random.seed(123)
torch.manual_seed(123)
torch.cuda.manual_seed(123)
torch.backends.cudnn.deterministic = True

在预处理步骤中,首先将文本数据转换为tokens序列,之后便可以将其传递到嵌入层。我将利用Keras包中提供的实用程序来进行预处理,利用torchtext包也同样可以实现。

from keras.preprocessing import text, sequence
## create tokens
tokenizer = Tokenizer(num_words = 1000)
tokenizer.fit_on_texts(x_train)
word_index = tokenizer.word_index
## convert texts to padded sequences
x_train = tokenizer.texts_to_sequences(x_train)
x_train = pad_sequences(x_train, maxlen = 70)

接下来,需要将tokens转换成向量。为此,利用预先训练过的GloVe词嵌入。我们将加载这些单词嵌入,并创建一个包含单词向量的嵌入矩阵。

GloVe:

https://github.com/stanfordnlp/GloVe

EMBEDDING_FILE = 'glove.840B.300d.txt'
embeddings_index = {}
for i, line in enumerate(open(EMBEDDING_FILE)):
   val = line.split()
   embeddings_index[val[0]] = np.asarray(val[1:], dtype='float32')
embedding_matrix = np.zeros((len(word_index) + 1, 300))
for word, i in word_index.items():
   embedding_vector = embeddings_index.get(word)
   if embedding_vector is not None:
       embedding_matrix[i] = embedding_vector

使用嵌入层和LSTM层定义模型架构:

class Modeldef __init__## Embedding Layer, Add parameter
       self.embedding = nn.Embedding(max_features, embed_size)
       et = torch.tensor(embedding_matrix, dtype=torch.float32)
       self.embedding.weight = nn.Parameter(et)
       self.embedding.weight.requires_grad = False
       self.embedding_dropout = nn.Dropout2d(0.1)
       self.lstm = nn.LSTM(300, 40)        
       self.linear = nn.Linear(40, 16)
       self.out = nn.Linear(16, 1)
       self.relu = nn.ReLU()
  def forward1)        
       linear = self.relu(self.linear(max_pool))
       out = self.out(linear)
       return out
model = Model()

创建训练和验证集:

from torch.utils.data import TensorDataset
## create training and validation split
split_size = int(0.8 * len(train_df))
index_list = list(range(len(train_df)))
train_idx, valid_idx = index_list[:split], index_list[split:]
## create iterator objects for train and valid datasets
x_tr = torch.tensor(x_train[train_idx], dtype=torch.long)
y_tr = torch.tensor(y_train[train_idx], dtype=torch.float32)
train = TensorDataset(x_tr, y_tr)
trainloader = DataLoader(train, batch_size=128)
x_val = torch.tensor(x_train[valid_idx], dtype=torch.long)
y_val = torch.tensor(y_train[valid_idx], dtype=torch.float32)
valid = TensorDataset(x_val, y_val)
validloader = DataLoader(valid, batch_size=128)

定义损失和优化器:

loss_function = nn.BCEWithLogitsLoss(reduction='mean')

optimizer = optim.Adam(model.parameters())

训练模型:

## run for 10 Epochs
for epoch in range(1, 11):
   train_loss, valid_loss = [], []
## training part
   model.train()
   for data, target in trainloader:
       optimizer.zero_grad()
       output = model(data)
       loss = loss_function(output, target.view(-1,1))
       loss.backward()
       optimizer.step()
       train_loss.append(loss.item())
   ## evaluation part
   model.eval()
   for data, target in validloader:
       output = model(data)
       loss = loss_function(output, target.view(-1,1))
       valid_loss.append(loss.item())

最后得到预测结果:

dataiter = iter(validloader)
data, labels = dataiter.next()
output = model(data)
_, preds_tensor = torch.max(output, 1)
preds = np.squeeze(preds_tensor.numpy())
Actual: [0 1 1 1 1 0 0 0 0]
Predicted: [0 1 1 1 1 1 1 1 0 0]

用例4:图像样式迁移

让我们来看最后一个用例,在这里我们将执行图形样式的迁移。这是我经历过的最有创意的项目之一,希望你也能玩得开心。样式迁移概念背后的基本理念是:

  • 从一幅图像中获取对象/内容

  • 从另一幅图像中获取样式/纹理

  • 生成二者混合的最终图像

“利用卷积网络进行图像样式迁移”这篇论文中对这一概念做了介绍,样式迁移的一个例子如下:

太棒了,对吧?让我们看看它在PyTorch中是如何实现的。这一进程包括六个步骤:

  • 从两个输入图像中提取低层特征。这可以使用VGG 19这样的预训练的深度学习模型。

from torchvision import models
# get the features portion from VGG19
vgg = models.vgg19(pretrained=True).features

# freeze all VGG parameters
for param in vgg.parameters():
   param.requires_grad_(False)
# check if GPU is available
device = torch.device("cpu")
if torch.cuda.is_available():
   device = torch.device("cuda")
vgg.to(device)
  • 将这两幅图像加载到设备上,并从VGG中获取特征。另外,也可以应用以下转换:调整张量的大小,以及值的归一化。

from torchvision import transforms as tf
def transformation400), tf.ToTensor(),
              tf.Normalize((0.44,0.44,0.44),(0.22,0.22,0.22))])
   img = tasks(img)[:3,:,:].unsqueeze(0)    
   return img
img1 = Image.open("image1.jpg").convert('RGB')
img2 = Image.open("image2.jpg").convert('RGB')
img1 = transformation(img1).to(device)
img2 = transformation(img2).to(device)
  • 现在,我们需要获得这两幅图像的相关特征。从第一个图像中,我们需要提取内容或与存在的对象相关的特征;从第二张图像中,我们需要提取与样式和纹理相关的特征。

对象相关特征:在最初的文章中,作者建议可以从网络的初始层中提取更有价值的对象和内容,这是因为在较高层上,信息空间变得更为复杂,像素信息细节在高层往往会丢失。

样式相关特征:为了从第二幅图像中获取样式和纹理信息,作者在不同层次上使用了不同特征之间的相关性,下文第4点对此作了详细解释。

在实现这一目标之前,让我们来看看一个典型的VGG 19模型的结构:

对象信息提取用到的是CONV42层,它位于第4个卷积块中,深度为512。对于样式的表达,用到的层是网络中每个卷积块的第一卷积层,即CONV11、CONV21、CONV31、CONV41和CONV51,这些层的选取纯粹是根据作者的经验来做出选择,我仅在本文中复制它们的结果。

def get_features'0': 'conv1_1', '5': 'conv2_1',  '10': 'conv3_1',
             '19': 'conv4_1', '21': 'conv4_2', '28': 'conv5_1'}
   x = image
   features = {}
   for name, layer in model._modules.items():
       x = layer(x)
       if name in layers:
           features[layers[name]] = x    
   return features
img1_features = get_features(img1, vgg)
img2_features = get_features(img2, vgg)
  • 正如前面提到的,作者使用不同层次的相关性来获得与样式相关的特征。这些特征的相关性由Gram矩阵G给出,其中G中的每个单元(i,j)都是层中向量特征映射i和j之间的内积。

def correlation_matrixreturn correlation
correlations = {l: correlation_matrix(img2_features[l]) for l in
                                                   img2_features}
  • 最终,可以利用这些特征和相关性进行样式转换。现在,为了将样式从一个图像转换到另一个图像,需要设置用于获取样式特征的每一层的权重。如上所述,由于初始层提供了更多的信息,因此可以为初始层设置更高的权重。此外,定义优化器函数和目标图像,也即是图像1的副本。

weights = {'conv1_1': 1.0, 'conv2_1': 0.8, 'conv3_1': 0.25,
          'conv4_1': 0.21, 'conv5_1': 0.18}

target = img1.clone().requires_grad_(True).to(device)
optimizer = optim.Adam([target], lr=0.003)
  • 启动损失最小化处理过程:即在循环中运行大量步骤,来计算与对象特征提取和样式特征提取相关的损失。利用最小化后的损失,更新网络参数,进一步修正目标图像。经过一些迭代之后,将生成更新后的图像。

for ii in range(1, 2001):
   ## calculate the content loss (from image 1 and target)
   target_features = get_features(target, vgg)
   loss = target_features['conv4_2'] - img1_features['conv4_2']
   content_loss = torch.mean((loss)**2)
   ## calculate the style loss (from image 2 and target)
   style_loss = 0

   for layer in weights:
       target_feature = target_features[layer]
       target_corr = correlation_matrix(target_feature)
       style_corr = correlations[layer]
       layer_loss = torch.mean((target_corr - style_corr)**2)
       layer_loss *= weights[layer]
       _, d, h, w = target_feature.shape
       style_loss += layer_loss / (d * h * w)
   total_loss = 1e6 * style_loss + content_loss
   optimizer.zero_grad()
   total_loss.backward()
   optimizer.step()

最后,我们可以看到预测的结果,在这里我只运行了一小部分迭代,还可以运行多达3000次迭代(如果计算资源足够多的话!)。

def tensor_to_image"cpu").clone().detach()
   image = image.numpy().squeeze()
   image = image.transpose(1, 2, 0)
   image *= np.array((0.22, 0.22, 0.22))
                      + np.array((0.44, 0.44, 0.44))
   image = image.clip(0, 1)
   return image
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
ax1.imshow(tensor_to_image(img1))
ax2.imshow(tensor_to_image(target))

以上就是如何用PyTorch快速准确地建立神经网络,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注创新互联-成都网站建设公司行业资讯频道。


网页名称:如何用PyTorch快速准确地建立神经网络-创新互联
网页URL:http://pwwzsj.com/article/dsjdpj.html