from mxnet import ndarray as nd
nd.zeros((3,4))
x = nd.ones((3,4))
nd.array([[1,2],[2,3]])
y = nd.random_normal(0,1,shape=(3,4))
y.shape
y.size
#操作符
x + y
x * y
nd.exp(y)
nd.dot(x,y.T)
#广播
a = nd.arange(3).reshape((3,1))
b = nd.arange(2).reshape((1,2))
print('a+b',a+b)
#跟nnumpy的转换
x = np.ones((2,3))
y = nd.array(x)
z = y.asnumpy()
print([z,y])
#替换操作
x = nd.ones((3,4))
y = nd.ones((3,4))
before = id(y)
y = y + x
id(y) == before
z = nd.zeros_like(x)
before = id(z)
z[:] = x + y
id(z) == before
MXNet提供autograd包来自动化求导过程,虽然大部分的深度学习框架要求编译计算图来自动求导,mxnet.autograd可以对正常的命令式程序进行求导,它每次在后端实时创建计算图从而可以立即得到梯度的计算方法
import mxnet.ndarray as nd
import mxnet.autograd as ag
#为变量附上梯度
x = nd.array([[1,2],[3,4]])
'''当进行求导的时候,我们需要一个地方来存x的导数,这个可以通过NDArray的方法attach_grad()来要求系统申请对应的空间'''
x.attach_grad()
'''默认条件下,MXNet不会自动记录和构建用于求导的计算图,我们需要使用autograd里的record()函数来显式地要求MXNet记录我们需要求导的程序'''
with ag.record():
y = x * 2
z = y * x
#接下来可以通过z.backward()来进行求导,如果z不是一个标量,那么z.backward()等价于nd.sum(z).backward()
z.backward()
x.grad == 4*x
'''对控制流求导
命令式的编程的一个便利之处是几乎可以对任意的可导程序进行求导,即使里面包含了python的控制流。考虑到下面程序,里面包含了控制流for和if,但循环迭代的次数和判断语句的执行都是取决于输入的值。不同的输入会导致这个程序的执行不一样(对计算图框架来说,这个对应于动态图,就是图的结构会根据输入数据不同而改变)'''
def f(a):
b = a * 2
while nd.norm(b).asscalar() < 1000:
b = b * 2
if nd.sum(b).asscalar() > 0:
c = b
else:
c = 100 * b
return c
#我们可以跟之前一样使用record记录和backward求导
a = nd.random_normal(shape=3)
a.attach_grad()
with ag.record():
c = f(a)
c.backward()
#头梯度和链式法则
with ag.record():
y = x * 2
z = y * x
head_gradient = nd.array([[10,1],[0.1,0.01]])
z.backward(head_gradient)
print(x.grad)
from mxnet import ndarray as nd
from mxnet import autograd
num_inputs = 2
num_examples = 1000
true_w = [2,-3.1]
true_b = 4.2
x = nd.random_normal(shape=(num_examples,num_inputs))
y = true_w[0] * x[:,0] + true_w[1] * x[:,1] + true_b
y += 0.01 * nd.random_normal(shape = y.shape)
#注意到x的每一行是一个长度为2的向量,而y的每一行是一个长度为1的向量(标量)
print(x[0:10],y[0:10])
'''数据读取
当我们开始训练神经网络的时候,我们需要不断读取数据块,这里我们定义一个函数它每次返回batch_size个随机的样本和对应的目标
我们通过python的yield来构造一个迭代器'''
import random
batch_size = 10
def data_iter():
#产生一个随机索引
idx = list(range(num_examples))
random.shuffle(idx)
for i in range(0,num_examples,batch_size):
j = nd.array(idx[i:min(i+batch_size,num_examples)])
yield nd.take(x,j),nd.take(y,j)
#读取第一个随机数据块
n = 0
for data,label in data_iter():
n = n + 1
print(n)
print(data,label)
break
#初始化模型参数
w = nd.random_normal(shape=(num_inputs,1))
b = nd.zeros((1,))
params = [w,b]
#之后训练时我们需要对这些参数求导来更新它们的值,所以我们需要创建它们的梯度
for param in params:
param.attach_grad()
#定义模型
#线性模型就是将输入和模型做乘法再加上偏移
def net(x):
return nd.dot(x,w) + b
#损失函数
#我们使用常见的平方误差来衡量预测目标和真实目标之间的差距
def square_loss(yhat,y):
#注意这里我们把y变形成yhat的形状来避免自动广播
return (yhat - y.reshape(yhat.shape)) ** 2
'''优化
虽然线性回归有显式解,但绝大部分模型并没有,所以我们这里通过随机梯度下降来求解
每一步,我们将模型参数沿着梯度的反方向走特定距离,这个距离一般叫学习率'''
def SGD(params,lr):
for param in params:
param[:] = param - lr * param.grad
'''训练
训练通常需要迭代数据数次,一次迭代里,我们每次随机读取固定数个数据点,计算梯度并更新模型参数'''
epochs = 5
learning_rate = 0.001
for e in range(epochs):
total_loss = 0
for data,label in data_iter():
with autograd.record():
output = net(data)
loss = square_loss(output,label)
loss.backward()
SGD(params,learning_rate)
total_loss += nd.sum(loss).asscalar()
print("epoch %d,average loss:%f" % (e,total_loss/num_examples))
#训练完成后我们可以比较学到的参数和真实参数
true_w,w
true_b,b
#结论:仅仅使用NDArray和autograd可以很容易地实现一个模型