第2章 感知机
2.3 感知机的实现
2.3.1 简单的实现
def AND(x1,x2):
w1,w2,theta = 0.5,0.5,0.7
tmp = x1*w1+x2*w2
if tmp <= theta:
return 0
elif tmp>theta:
return 1
2.3.2 导入权重和偏置
import numpy as np
x=np.array([0,1]) # 创建数组对象
print(x)
w=np.array((0.5,0.5))
print(w)
b=-0.7 # b = -theta
print(w*x) # [0. 0.5]
y=np.sum(w*x)+b
print(y) # -0.19999999999999996,大约为0.2
2.3.3 使用权重和偏置实现
权重,即参数
偏置,调整神经元激活程度的容易程度。
(因为,如果b=-0.1,则说明加权和只要大于0.1,神经元就会被激活;
而如果b=-20,则加权和需得大于20,神经元才会被激活)
# 与门
import numpy as np
def AND(x1,x2):
x=np.array([x1,x2])
w=np.array([0.5,0.5])
b=-0.7
tmp = np.sum(w*x+b)
if(y<=0):
return 0
else:
return 1
# 或门
import numpy as np
def NAND(x1,x2):
x=np.array([x1,x2])
w=np.array([-0.5,-0.5])
b=0.7
tmp=np.sum(w*x)+b
if(tmp<=0):
return 0
else:
return 1
# 非门
def OR(x1,x2):
x=np.array([x1,x2])
w=np.array([0.5,0.5])
b=-0.2
tmp=np.sum(w*x)+b
if(tmp<=0):
return 0
else:
return 1
2.5 多层感知机
2.5.1 二层感知机构建 异或门
理论上两层感知机就能构建计算机了,因为研究证明2层计算机可以表示任意函数(严格说是激活函数使用了非线性的sigmoid函数的感知机)。
# 异或门的实现(由与、或、非门综合构成)
def XOR(x1,x2):
s1=NAND(x1,x2)
s2=OR(x1,x2)
y=AND(s1,s2)
return y
第三章 神经网络
- 好消息:即使是复杂的函数,也可以通过多层感知机表示
坏消息:设置权重的工作,还是由人工进行。 - 神经网络的出现,就是为了解决这里的坏消息。
------>神经网络可以自动地从数据中学习到 合适的权重参数。 - 激活函数的作用就是:决定如何来激活输入信号的总和。比如,求和的形式。
(激活函数也是 连接 感知机 和 神经网络的 桥梁)
3.2 激活函数
神经网络的激活函数必须是非线性函数。
如果使用线性函数,那么加深神经网络的层数就没有意义了。
3.2.2 阶跃函数的实现
# x只能接受实数(浮点数),但x不能取Numpy数组!如step_function([1.0,2.0])
def step_function(x):
if x>0:
return 1
else:
return 0
将上面的函数修改为 支持Numpy数组的形式:
# 前提:对Numpy数组进行不等号运算后,数组的每个元素都会进行 不等号运算
import numpy as np
x=np.array([-1.0,1.0,2.0])
print(x) # [-1. 1. 2.]
y=x>0
print(y) # [False True True],即y是一个布尔数组
y=y.astype(np.int64) # .astype()用于将数组的元素类型转换为指定的类型
print(y) # [0 1 1]
# Numpy技巧实现阶跃函数
def step_function(x):
y=x>0
return y.astype(np.int64)
3.2.3 阶跃函数的图像
import matplotlib.pyplot as plt
import numpy as np
def step_function(x):
return np.array(x>0,dtype=np.int64)
x=np.arange(-5.0,5.0,0.1) # 生成数组
y=step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1) # 指定y轴范围
plt.show()

3.2.4 sigmoid函数的实现
import matplotlib.pyplot as plt
import numpy as np
def sigmoid(x): # 由于Numpy数组的广播功能,该函数可以接受Numpy数组
return 1/(1+np.exp(-x))
x=np.arange(-5.0,5.0,1.0) # 生成数组
y=sigmoid(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1) # 指定y轴范围
plt.show()

3.2.7 ReLU函数
import matplotlib.pyplot as plt
import numpy as np
def relu(x):
return np.maximum(0,x)
x=np.arange(-5.0,5.0,1.0) # 生成数组
y=relu(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1) # 指定y轴范围
plt.show()

3.3 多维数组的运算
3.3.1当A是二维矩阵,B是一维数组时
总结:np.dot() 运算会根据矩阵的维度和广播机制处理形状不完全匹配的情况,只要满足矩阵乘法的条件(即左侧矩阵的列数 等于 右侧矩阵的行数)。
3.3.2 神经网络的内积
import numpy as np
x=np.array([1,2])
print(x.shape)
w=np.array([[1,3,5],[2,4,6]]) # 向量的维度大小与矩阵行数一直,则左乘;
print(w.shape)
Y=np.dot(x,w)
print(Y) # [ 5 11 17]
3.4 3层神经网络的实现
- 普通表示
import numpy as np
# 隐藏层激活函数
def sigmoid(x): # h(x)
return 1/(1+np.exp(-x))
x=np.array([1.0,0.5])
w1=np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]]) # 一行表示左侧一个神经元发射的参数
b1=np.array([0.1,0.2,0.3])
# 输入层-->第1层之间的信号传递
a1=np.dot(x,w1)+b1
print(a1) # [0.3 0.7 1.1]
z1=sigmoid(a1)
print(z1) # [0.57444252 0.66818777 0.75026011]
# 第1层-->第2层之间的信号传递
w2=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
b2=np.array([0.1,0.2])
a2=np.dot(z1,w2)+b2
print(a2) # [0.51615984 1.21402696]
z2=sigmoid(a2)
print(z2) # [0.62624937 0.7710107 ]
# 第2层-->输出层之间的信号传递
# 输出层的激活函数也隐藏层不同
def identity_function(x): # 恒等函数sigma(x)
return x
w3=np.array([[0.1,0.3],[0.2,0.4]])
b3=np.array([0.1,0.2])
a3=np.dot(z2,w3)+b3
print(a3)
y=identity_function(a3)
print(y)
- 神经网络表示
# 隐藏层激活函数
def sigmoid(x): # h(x)
return 1/(1+np.exp(-x))
def init_network():
network = {}
network['w1'] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
network['b1'] = np.array([0.1,0.2,0.3])
network['w2'] = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network['b2'] = np.array([0.1,0.2])
network['w3'] = np.array([[0.1,0.3],[0.2,0.4]])
network['b3'] = np.array([0.1,0.2])
return network
# 前向传递
def forward(network,x):
w1,w2,w3 = network['w1'],network['w2'],network['w3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1=np.dot(x,w1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,w2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,w3)+b3
y=identity_function(a3)
return y
network=init_network()
x=np.array([1.0,0.5])
y=forward(network,x)
print(y)
3.5 输出层的设计(激活函数的选择)
神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出层的激活函数。
回归问题—>恒等函数
分类问题—>softmaxh函数
import numpy as np
# 计算有溢出风险
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return a
softmax的指数函数运算时,加上/减去 某个常数不会改变运算结果。
import numpy as np
# softmax的指数函数运算时,加上/减去 某个常数不会改变运算结果。
# 常数取输入信号的最大值
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c) # 避免溢出的对策
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return y
3.6 例子–>手写数字识别
3.6.1 MNIST数据集
file:内置函数,返回这个py文件路径
os.path.abspath(file):返回这个文件的绝对路径
os.path.dirname:返回这个文件的上一层目录的路径名
os.pardir返回父目录的字符串,表示父目录的符号(…)
import sys,os
# os.pardir返回父目录的字符串;导入父目录中的文件而进行的设定
sys.path.append("D:/GitRepository/1.start python") # os.pardir,使用绝对路径
from dataset.mnist import load_mnist
import numpy as np
(x_train,y_train),(x_test,y_test) = load_mnist(flatten = True,normalize = False)
print(x_train.shape)
面临的问题:运行代码发现MNIST在给定网站数据不存在,以至于下载不下来。
解决:通过Mnist数据集中,百度网盘链接下载下来4个.gz文件。将它们放在load_mnist文件所在位置的父目录中,即dataset目录中。
(因为执行load_mnist函数时,会检查.gz文件是否已经在本地,即可跳过远程下载这个步骤。)
3.6.2 显示图片
import sys,os
# # os.pardir返回父目录的字符串;导入父目录中的文件而进行的设定
sys.path.append("D:/GitRepository/1.start python") # os.pardir,使用绝对路径
from dataset.mnist import load_mnist
import numpy as np
from PIL import Image
(x_train,y_train),(x_test,y_test) = load_mnist(flatten = True,normalize = False)
def img_show(img):
pil_img = Image.fromarray(np.uint8(img)) # 将 NumPy 数组转换为 Image 对象.from array
# .uint8():确保图像数据格式正确,通常图像的每个像素值在 0 到 255 之间
pil_img.show() # 显示图像
img = x_train[0]
lable = y_train[0]
print(lable)
print(img.shape)
img = img.reshape(28,28)
print(img.shape)
img_show(img)
3.6.3 神经网络的推理处理
- 单个数据
import sys,os
# # os.pardir返回父目录的字符串;导入父目录中的文件而进行的设定
sys.path.append("D:/GitRepository/1.start python") # os.pardir,使用绝对路径
from dataset.mnist import load_mnist
import numpy as np
import pickle
def sigmoid(x): # h(x)
return 1/(1+np.exp(-x))
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c) # 避免溢出的对策
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return y
def get_data():
(x_train,y_train),(x_test,y_test) = load_mnist(normalize=True,flatten = True,one_hot_label=False)
return x_test,y_test
def init_network():
with open(r"D:\GitRepository\1.start python\ch03\sample_weight.pkl",'rb') as f:
network=pickle.load(f)
return network
def predict(network,x):
w1,w2,w3 = network['W1'],network['W2'],network['W3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1=np.dot(x,w1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,w2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,w3)+b3
y=softmax(a3)
return y
x,y=get_data()
network=init_network()
accuracy_cnt = 0
for i in range(len(x)):
t=predict(network,x[i])
p=np.argmax(t) # 获取概率最高的元素的索引
if p==y[i]:
accuracy_cnt +=1
print("Accuracy:"+str(float(accuracy_cnt)/len(x))) # Accuracy:0.9352
- 批处理

import sys,os
# # os.pardir返回父目录的字符串;导入父目录中的文件而进行的设定
sys.path.append("D:/GitRepository/1.start python") # os.pardir,使用绝对路径
from dataset.mnist import load_mnist
import numpy as np
from PIL import Image
import pickle
def sigmoid(x): # h(x)
return 1/(1+np.exp(-x))
def softmax(a):
c = np.max(a)
exp_a = np.exp(a-c) # 避免溢出的对策
sum_exp_a = np.sum(exp_a)
y = exp_a/sum_exp_a
return y
def get_data():
(x_train,y_train),(x_test,y_test) = load_mnist(normalize=True,flatten = True,one_hot_label=False)
return x_test,y_test
def init_network():
with open(r"D:\GitRepository\1.start python\ch03\sample_weight.pkl",'rb') as f:
network=pickle.load(f)
return network
def predict(network,x):
w1,w2,w3 = network['W1'],network['W2'],network['W3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1=np.dot(x,w1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,w2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,w3)+b3
y=softmax(a3)
return y
x,y=get_data()
batch_size=100
network=init_network()
accuracy_cnt = 0
for i in range(0,len(x),batch_size):
x_batch = x[i:i+batch_size]
t_batch=predict(network,x_batch)
p=np.argmax(t_batch,axis=1) # 获取概率最高的元素的索引
accuracy_cnt += np.sum(p==y[i:i+batch_size])
print("Accuracy:"+str(float(accuracy_cnt)/len(x))) # Accuracy:0.9352
第四章 神经网络的 学习
引入损失函数这一指标,找出使得损失函数的值最小的权重参数。
4.2 损失函数
4.2.1 均方误差
import numpy as np
# 均方误差
def mean_squared_error(y,t):
return 0.5*np.sum((y-t)**2)
4.2.2 交叉熵误差
交叉熵误差:由于正确解对应的输出结果决定
正确解对应的输出越大,误差越接近于0;对应的输出越小,误差越大
def cross_entropy_error(y,t):
delta = 1e-7
return -np.sum(t*np.log(y+delta)) # 加上delta是为了避免出现 log(0)
4.2.3 mini-batch学习
所有训练数据的损失函数总和 / n —> 单个数据的平均损失函数
为了避免大规模的计算,可从全部数据中选择一部分,作为全部数据的**“近似”**。
np.random.choice可以 从指定的数字中 随机选择 想要的数字。
如:np.random.choice(60000,10)表示:会从0~59999之间随机选择10个数字
import numpy as np
train_size = x_train.shape[0] # 784
batch_size = 10
batch_mask = np.random.choice(train_size,batch_size)
x_batch = x_train(batch_mask)
y_batch = y_train(batch_mask)
4.2.4 mini-batch版交叉熵误差的实现
- t为one-hot形式 【对应相乘】
y = np.array([[0.1, 0.3, 0.6]]) # 模型预测概率
t = np.array([[0, 0, 1]]) # one-hot 真实标签
y*t = [[0,0,0.6]] # 对应相乘
import numpy as np
# 非one-hot版本
def cross_entropy_error(y,t):
if y.ndim ==1: # 求单个数据的交叉熵损失函数时
y = y.reshape(1,y.size) # # 将y从一维转换为二维
t = t.reshape(1,t.size)
batch_size = y.shape[0] # 样本数。就是因为一步才有了前面的if判断
return -np.sum(t*np.log(y+1e-7))/batch_size # 由于 广播功能 可以直接计算
- t为 非one-hot版本 【二维坐标】
y[np.arange(batch_size),t] :从 二维矩阵 中 根据坐标取出 正确值对应的输出值。
具体地,[np.arange(batch_size),t]中,np.arange(batch_size)生成的每个数,都会 依次 与 t 组合成二维坐标。
如:[y[0,3],y[1,7],y[2,0],y[3,9],y[4,4]]
# 适用于 非one-hot 编码的标签
def cross_entropy_error(y,t):
if y.ndim ==1: # 求单个数据的交叉熵损失函数时
y = y.reshape(1,y.size) # # 将y从一维转换为二维
t = t.reshape(1,t.size)
batch_size = y.shape[0] # 样本数。就是因为一步才有了前面的if判断
return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size
4.2.5 为什么要设定损失函数
如果导数为负,通过使该权重参数向正方向改变,可以减小损失函数的值;
如果导数为正,通过使该权重参数向负方向改变,可以增大损失函数的值。
当导数为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变。
4.3 数值微分
4.3.1 导数
def numerical_diff(f,x):
h=1e-4 # 0.0001
return (f(x+h)-f(x-h))/(2*h) # 可以确保x在中间,也就是所求趋近于x处的导数
4.3.2 偏导数
def numerical_diff(f,x):
h=1e-4 # 0.0001
return (f(x+h)-f(x-h))/(2*h) # 可以确保x在中间,也就是所求趋近于x出的导数
def function_tmp1(x0):
return x0*x0+4**2
print(numerical_diff(function_tmp1,3))
4.4 梯度.
有全部变量的偏导数汇总而成的向量称为梯度。
负梯度方向是梯度法中变量的更新方向:随着x的增大,函数会减小的方向。
梯度:各点处函数值减小最多的方向,无法保证所指的方向就是函数的最小值或者真正应该前进的方向。
- 计算梯度
# 基于一个参数计算梯度
import numpy as np
def numerical_gradient(f,x):
h=1e-4
grad = np.zero_like(x) # 返回一个与输入数组 x 具有 相同形状和数据类型 的全零数组
# 为每一个x自变量计算偏导
for idx in range(len(x)):
tmp = x[idx]
# f(x+h)
x[idx] = tmp+h
fxh1=f(x)
# f(x-h)
x[idx] = tmp-h
fxh2 = f(x)
grad[idx] = (fxh1-fxh2)/(2*h)
x[idx] = tmp # 为下一个位置偏导的计算而还原
return grad
- 实现梯度下降法
# 基于一个参数计算梯度
import numpy as np
def numerical_gradient(f,x):
h=1e-4
grad = np.zeros_like(x) # 返回一个与输入数组 x 具有 相同形状和数据类型 的全零数组
# 为每一个x自变量计算偏导
for idx in range(len(x)):
tmp = x[idx]
# f(x+h)
x[idx] = tmp+h
fxh1=f(x)
# f(x-h)
x[idx] = tmp-h
fxh2 = f(x)
grad[idx] = (fxh1-fxh2)/(2*h)
x[idx] = tmp # 为下一个位置偏导的计算而还原
return grad
# 开始下降
def gradent_descent(f,init_x,lr=0.01,step_num=100):
x = init_x
# 迭代次数
for i in range(step_num):
grad = numerical_gradient(f,x) # 计算梯度
x -= lr*grad
return x
4.4.2 神经网络的梯度
import sys,os
sys.path.append(os.pardir)
import numpy as np
from common.functions import softmax,cross_entropy_error
from common.gradient import numerical_gradient
# 求梯度
class simpleNet:
def __init__(self):
self.w = np.random.randn(2,3) # 根据正态分布进行初始化,参数表示维度
def predict(self,x):
return np.dot(x,self.w)
def loss(self,x,t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y,t)
return loss
net = simpleNet()
x = np.array([0.6,0.9])
t = np.array([0,1])
def f(w):
return net.loss(x,t)
dw = numerical_gradient(f,net.w) # 梯度
4.5 学习算法的实现
步骤1:mini-batch:由于mini batch数据是随机选择的,所以又称 随机梯度下降法,由SGD函数实现。
步骤2:计算梯度
步骤3:更新参数
步骤4:重复以上步骤
4.5.1 2层神经网络的类
如何设置权重参数影响神经网络能否学习成功:
权重 使用 符合高斯分布的随机数进行初始化;
偏置 实验 0 进行初始化。
import sys,os
sys.path.append("D:/GitRepository/1.start python")
from common.functions import *
from common.gradient import numerical_gradient
class TwoLayerNet():
def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
# 初始化权重
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size,hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size,output_size)
self.params['b2'] = np.zeros(output_size)
def predict(self,x):
w1,w2 = self.params['W1'],self.params['W2']
b1,b2 = self.params['b1'],self.params['b2']
a1 = np.dot(x,w1)+b1
z1 = sigmoid(a1)
a2 = np.dot(z1,w2)+b2
y = softmax(a2)
return y
# x是输入数据;t是正确答案
def loss(self,x,t):
y = self.predict(x)
return cross_entropy_error(y,t) # 不用对y进行np.argmax的操作,因为交叉熵损失函数 可以直接计算概率分布之间的差异
# 计算梯度,调用求导函数。(梯度计算-->基于一个变量求导)
def gradient_gradients(self,x,t):
loss_w = lambda w:self.loss(w,t) # lambda式子,可以定义只有一个参数的函数
# 将每个参数 的 导数 存入grads中,组成梯度向量
grads = {}
grads['W1'] = numerical_gradient(loss_w,self.params['W1']) # numerical_gradient 基于 数值微分 计算梯度。
grads['b1'] = numerical_gradient(loss_w,self.params['b1'])
grads['w2'] = numerical_gradient(loss_w,self.params['w2'])
grads['b1'] = numerical_gradient(loss_w,self.params['b2'])
return grads
def accuracy(self,x,t):
y = self.predict(x)
y = np.argmax(y,axis=1)
t = np.argmax(t,axis=1)
accuracy = np.sum(y==t)/float(x.shape[0])
return accuracy
net = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
x=np.random.rand(100,784) # 伪输入数据100笔
t=np.random.rand(100,10)
4.5.3 mini-batch的实现
import sys
import numpy as np
sys.path.append("D:/GitRepository/1.start python")
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label = True)
train_loss_list = []
# 超参数
iters_num = 10000 # 梯度更新次数为10000次
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
network = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
for i in range(iters_num):
# 获取mini-batch
batch_mask = np.random.choice(train_size,batch_size) # 随机选择mini-batch序号,所以不会遍历到每一个训练数据
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# 计算梯度(向量)
grad = network.numerical_gradients(x_batch,t_batch)
# grad = network.gradient(x_batch,t_batch) # 高速版
# 更新参数
for key in ('W1','b1','W2','b2'):
network.params[key] -= learning_rate * grad[key]
# 记录学习过程
loss = network.loss(x_batch,t_batch)
train_loss_list.append(loss)
4.5.4 基于测试数据的评价
一个epoch,即遍历一次训练数据。
import sys
import numpy as np
sys.path.append("D:/GitRepository/1.start python")
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label = True)
train_loss_list = [] # 记录训练过程
train_acc_list = []
test_acc_list = []
# 平均每个epoch的重复次数(多少次才能把训练集遍历完)
iter_per_epoch = max(train_size/batch_size,1)
# 超参数
iters_num = 10000 # 梯度更新次数为10000次
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1
network = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
for i in range(iters_num):
# 获取mini-batch
batch_mask = np.random.choice(train_size,batch_size) # 随机选择mini-batch序号
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
# 计算梯度(向量)
grad = network.numerical_gradients(x_batch,t_batch)
# grad = network.gradient(x_batch,t_batch) # 高速版
# 更新参数
for key in ('W1','b1','W2','b2'):
network.params[key] -= learning_rate * grad[key]
# 记录学习过程
loss = network.loss(x_batch,t_batch)
train_loss_list.append(loss)
# 计算每个epoch的识别精度(验证效果)
if i % iter_per_epoch == 0: # 遍历完一遍训练数据
train_acc = network.accuracy(x_batch,t_batch)
test_acc = network.accuracy(x_test,t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print("train acc, test acc | "+str(train_acc)+", "+str(test_acc))
总结
- loss(x,t):return 损失函数计算函数。
- numerical_gradient(x,t):return grad.
基于一个参数计算导数,由于参数可能是一个矩阵/ 向量,所以将 基于数值微分求导的函数 封装为新的函数。 - numerical_gradient(x,t):return grads.
为 损失函数中 的每一个参数计算导数,存入grads中,组成梯度向量grads。


1699

被折叠的 条评论
为什么被折叠?



