Mxnet(三)

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

from mxnet import nd
from mxnet.gluon import nn

net = 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的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
        # 参数,如“模型参数的访问、初始化和共享”一节将介绍的模型参数params
        super(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 /= 2
        if x.norm().asscalar() < 0.8:
            x *= 10
        return 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 nd

x = 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 nn

class 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 <= 1
    keep_prob = 1 - drop_prob
    # 这种情况下把全部元素都丢弃
    if keep_prob == 0:
        return X.zeros_like()
    mask = nd.random.uniform(0, 1, X.shape) < keep_prob
    return mask * X / keep_prob

a = 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,而非论文中的1000
        nn.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 blk

def 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)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值