Mxnet(三)

2023-09-25 11 0

一、用Sequential来构建网络非常方便,并且不要忘记初始化

from mxnet import nd
from mxnet.gluon import nnnet = nn.Sequential()
with net.name_scope():net.add(nn.Dense(256,activation = "relu"))net.add(nn.Dense(10))net.initialize()
x = nd.random_normal(shape = (4,20))
y = net(x)

二、如果想自己实现一个层,可以用下面的方法

class MLP(nn.Block):# 声明带有模型参数的层,这里声明了两个全连接层def __init__(self, **kwargs):# 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数# 参数,如“模型参数的访问、初始化和共享”一节将介绍的模型参数paramssuper(MLP, self).__init__(**kwargs)self.hidden = nn.Dense(256, activation='relu')  # 隐藏层self.output = nn.Dense(10)  # 输出层# 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出def forward(self, x):return self.output(self.hidden(x))net1 = MLP()
net1.initialize()
y = net1(x)

虽然Sequential类可以使模型构造更加简单,且不需要定义forward函数,但直接继承Block类可以极大地拓展模型构造的灵活性。下面我们构造一个稍微复杂点的网络FancyMLP。在这个网络中,我们通过get_constant函数创建训练中不被迭代的参数,即常数参数。在前向计算中,除了使用创建的常数参数外,我们还使用NDArray的函数和Python的控制流,并多次调用相同的层。

class FancyMLP(nn.Block):def __init__(self, **kwargs):super(FancyMLP, self).__init__(**kwargs)# 使用get_constant创建的随机权重参数不会在训练中被迭代(即常数参数)self.rand_weight = self.params.get_constant('rand_weight', nd.random.uniform(shape=(20, 20)))self.dense = nn.Dense(20, activation='relu')def forward(self, x):x = self.dense(x)# 使用创建的常数参数,以及NDArray的relu函数和dot函数x = nd.relu(nd.dot(x, self.rand_weight.data()) + 1)# 复用全连接层。等价于两个全连接层共享参数x = self.dense(x)# 控制流,这里我们需要调用asscalar函数来返回标量进行比较while x.norm().asscalar() > 1:x /= 2if x.norm().asscalar() < 0.8:x *= 10return x.sum()net3 = FancyMLP()
net3.initialize()
y = net3(x)

 

三、Sequential和自定义的也可以混合使用

class RecMLP(nn.Block):def __init__(self, **kwargs):super(RecMLP, self).__init__(**kwargs)self.net = nn.Sequential()with self.name_scope():self.net.add(nn.Dense(129,activation = "relu"))self.net.add(nn.Dense(64,activation = "relu"))self.dense = nn.Dense(256)def forward(self, x):return self.net(nn.relu(self.dense))rec_mlp = nn.Sequential()
rec_mlp.add(RecMLP())
rec_mlp.add(nn.Dense(64,activation = "relu"))
rec_mlp.add(RecMLP())
rec_mlp.add(nn.Dense(10))
print(rec_mlp)

用以上这几种方式可以任意且便捷地定义网络层。

 

四、关于网络里的参数

可以通过【标号】来访问每一个层的名字、shape

w = net[0].weight
b = net[0].bias
print([w,b])

可以访问模型参数的数值和梯度

print(w.data())
print(w.grad())
print(b.data())
print(b.grad())

共享模型参数,当我们想让两个层的参数一致时,可以通过param来手动指定参数,而不是让系统自动生成

net4 = nn.Sequential()
with net4.name_scope():net4.add(nn.Dense(4,in_units=4,activation = "relu"))net4.add(nn.Dense(4,in_units = 4,activation = "relu",params = net4[-1].params))net4.initialize()
print(net4[0].weight.data())
print(net4[1].weight.data())

读写ndarray,用save和load比较方便

from mxnet import ndx = nd.ones(3)
y = nd.zeros(4)
filename = "data/test1.params"
nd.save(filename,[x,y])
#再把存好的文件读进来
a,b = nd.load(filename)
print(a,b)

 

五、下面写一个自定义网络

from mxnet import gluon, nd
from mxnet.gluon import nnclass CenteredLayer(nn.Block):def __init__(self, **kwargs):super(CenteredLayer, self).__init__(**kwargs)def forward(self, x):return x - x.mean()net5 = nn.Sequential()
with net5.name_scope():net5.add(nn.Dense(128))net5.add(nn.Dense(64))net5.add(CenteredLayer())net5.initialize()
y = net5(nd.random.uniform(shape=(4, 8)))
y.mean().asscalar()

 

六、Dropout方法

dropout会把部分参数置为0,从而随机简化网络。

def dropout(X, drop_prob):assert 0 <= drop_prob <= 1keep_prob = 1 - drop_prob# 这种情况下把全部元素都丢弃if keep_prob == 0:return X.zeros_like()mask = nd.random.uniform(0, 1, X.shape) < keep_probreturn mask * X / keep_proba = nd.arange(20).reshape((4,5))
a = dropout(a,0.5)

 

七、AlexNet

net = nn.Sequential()
# 使用较大的11 x 11窗口来捕获物体。同时使用步幅4来较大幅度减小输出高和宽。这里使用的输出通
# 道数比LeNet中的也要大很多
net.add(nn.Conv2D(96, kernel_size=11, strides=4, activation='relu'),nn.MaxPool2D(pool_size=3, strides=2),# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数nn.Conv2D(256, kernel_size=5, padding=2, activation='relu'),nn.MaxPool2D(pool_size=3, strides=2),# 连续3个卷积层,且使用更小的卷积窗口。除了最后的卷积层外,进一步增大了输出通道数。# 前两个卷积层后不使用池化层来减小输入的高和宽nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),nn.Conv2D(384, kernel_size=3, padding=1, activation='relu'),nn.Conv2D(256, kernel_size=3, padding=1, activation='relu'),nn.MaxPool2D(pool_size=3, strides=2),# 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合nn.Dense(4096, activation="relu"), nn.Dropout(0.5),nn.Dense(4096, activation="relu"), nn.Dropout(0.5),# 输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000nn.Dense(10))print(net)

AlexNet的整体结构如下,还是比较清楚的

 

 

 

八、VGG

def vgg_block(num_convs, num_channels):blk = nn.Sequential()for _ in range(num_convs):blk.add(nn.Conv2D(num_channels, kernel_size=3,padding=1, activation='relu'))blk.add(nn.MaxPool2D(pool_size=2, strides=2))return blkdef vgg(conv_arch):net = nn.Sequential()# 卷积层部分for (num_convs, num_channels) in conv_arch:net.add(vgg_block(num_convs, num_channels))# 全连接层部分net.add(nn.Dense(4096, activation='relu'), nn.Dropout(0.5),nn.Dense(4096, activation='relu'), nn.Dropout(0.5),nn.Dense(10))return net

VGG关键是有一个vgg_block,然后搭建网络的过程中会重复用这个block。VGG16相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。

代码编程
赞赏

相关文章

【C】浅析 #define 宏和函数的区别
【C】浅析 关键字
【C】库函数之 sqrt
【C】折半(二分)查找
fio_generate_plots
【Linux】进程的调度算法