第4章(4.4~4.6节)自定义层与计算【深度学习计算】--动手学深度学习【Tensorflow2.0版本】

项目地址:https://github.com/TrickyGo/Dive-into-DL-TensorFlow2.0
UC 伯克利李沐的《动手学深度学习》开源书一经推出便广受好评。很多开发者使用了书的内容,并采用各种各样的深度学习框架将其复现。
现在,《动手学深度学习》书又有了一个新的复现代码版本——TensorFlow2.0 版,短时间内成为了github上千star项目,欢迎关注。

4.1 自定义层

深度学习的一个魅力在于神经网络中各式各样的层,例如全连接层和后面章节中将要介绍的卷积层、池化层与循环层。虽然tf.keras提供了大量常用的层,但有时候我们依然希望自定义层。本节将介绍如何自定义一个层,从而可以被重复调用。

import tensorflow as tf
import numpy as np
print(tf.__version__)
2.0.0
X = tf.random.uniform((2,20))

4.4.1 custom layer without parameters

我们先介绍如何定义一个不含模型参数的自定义层。事实上,这和[“模型构造”]一节中介绍的使用tf.keras.Model类构造模型类似。下面的CenteredLayer类通过继承tf.keras.layers.Layer类自定义了一个将输入减掉均值后输出的层,并将层的计算定义在了call函数里。这个层里不含模型参数。

class CenteredLayer(tf.keras.layers.Layer):
    def __init__(self):
        super().__init__()

    def call(self, inputs):
        return inputs - tf.reduce_mean(inputs)

我们可以实例化这个层,然后做前向计算。

layer = CenteredLayer()
layer(np.array([1,2,3,4,5]))
<tf.Tensor: id=11, shape=(5,), dtype=int32, numpy=array([-2, -1,  0,  1,  2])>

我们也可以用它来构造更复杂的模型。

net = tf.keras.models.Sequential()
net.add(tf.keras.layers.Flatten())
net.add(tf.keras.layers.Dense(20))
net.add(CenteredLayer())

Y = net(X)
Y
<tf.Tensor: id=42, shape=(2, 20), dtype=float32, numpy=
array([[-0.2791378 , -0.80257636, -0.8498672 , -0.8917849 , -0.43128002,
         0.2557137 , -0.51745236,  0.31894356,  0.03016172,  0.5299317 ,
        -0.094203  , -0.3885942 ,  0.6737736 ,  0.5981153 ,  0.30068082,
         0.42632163,  0.3067779 ,  0.07029241,  0.0343143 ,  0.41021633],
       [ 0.0257766 , -0.4703896 , -0.9074424 , -1.2818251 ,  0.17860745,
         0.11847494, -0.14939149,  0.20248316, -0.140678  ,  0.6033463 ,
         0.13899392, -0.08732668,  0.08497022,  0.8094018 ,  0.20579913,
         0.40613335,  0.2509889 ,  0.34718364, -0.6298219 ,  0.59436864]],
      dtype=float32)>

下面打印自定义层各个输出的均值。因为均值是浮点数,所以它的值是一个很接近0的数。

tf.reduce_mean(Y)
<tf.Tensor: id=44, shape=(), dtype=float32, numpy=-2.9802323e-09>

4.4.2 custom layer with parameters

我们还可以自定义含模型参数的自定义层。其中的模型参数可以通过训练学出。

class myDense(tf.keras.layers.Layer):
    def __init__(self, units):
        super().__init__()
        self.units = units

    def build(self, input_shape):     # 这里 input_shape 是第一次运行call()时参数inputs的形状
        self.w = self.add_weight(name='w',
            shape=[input_shape[-1], self.units], initializer=tf.random_normal_initializer())
        self.b = self.add_weight(name='b',
            shape=[self.units], initializer=tf.zeros_initializer())

    def call(self, inputs):
        y_pred = tf.matmul(inputs, self.w) + self.b
        return y_pred

下面,我们实例化MyDense类并访问它的模型参数。我们可以直接使用自定义层做前向计算。

dense = myDense(3)
dense(X)
dense.get_weights()
[array([[ 0.05307531, -0.01968029,  0.00317079],
        [-0.03745286, -0.0031012 , -0.0925727 ],
        [ 0.00653961, -0.0849395 , -0.00591413],
        [-0.03926834,  0.03737333, -0.08176559],
        [-0.02961348,  0.00735149, -0.04053285],
        [-0.0769348 , -0.01365675,  0.04430145],
        [ 0.05790468,  0.06002709,  0.00588025],
        [ 0.00912714, -0.04544574, -0.08150417],
        [ 0.01794734, -0.06478786, -0.0466853 ],
        [ 0.0007794 ,  0.07972597,  0.01827623],
        [ 0.04688237,  0.040658  ,  0.04173873],
        [ 0.07974287, -0.01226464,  0.03872328],
        [ 0.023996  , -0.044014  ,  0.01851312],
        [-0.04491149,  0.00450119,  0.03688556],
        [ 0.01733875, -0.01641337,  0.06909126],
        [-0.07539   , -0.0878872 ,  0.0091918 ],
        [-0.00092481, -0.06399333,  0.00150875],
        [-0.01826238, -0.06126164, -0.05938709],
        [ 0.04794892,  0.03742057, -0.0018529 ],
        [ 0.03086024,  0.00513093, -0.04271856]], dtype=float32),
 array([0., 0., 0.], dtype=float32)]

我们也可以使用自定义层构造模型。

net = tf.keras.models.Sequential()
net.add(myDense(8))
net.add(myDense(1))

net(X)
<tf.Tensor: id=121, shape=(2, 1), dtype=float32, numpy=
array([[-0.00446665],
       [-0.0158301 ]], dtype=float32)>

注:本节除了代码之外与原书基本相同,原书传送门

4.5 读取和存储

到目前为止,我们介绍了如何处理数据以及如何构建、训练和测试深度学习模型。然而在实际中,我们有时需要把训练好的模型部署到很多不同的设备。在这种情况下,我们可以把内存中训练好的模型参数存储在硬盘上供后续读取使用。

import tensorflow as tf
import numpy as np
print(tf.__version__)
2.0.0

4.5.1 load and save NDarray

我们可以直接使用save函数和load函数分别存储和读取。下面的例子创建了tensorx,并将其存在文件名同为x的文件里。

import numpy as np

x = tf.ones(3)
x
<tf.Tensor: id=2, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>

然后我们将数据从存储的文件读回内存。

np.save('x.npy', x)
x2 = np.load('x.npy')
x2
array([1., 1., 1.], dtype=float32)

我们还可以存储一列tensor并读回内存。

y = tf.zeros(4)
np.save('xy.npy',[x,y])
x2, y2 = np.load('xy.npy', allow_pickle=True)
(x2, y2)
(<tf.Tensor: id=6, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>,
 <tf.Tensor: id=7, shape=(4,), dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>)

我们甚至可以存储并读取一个从字符串映射到tensor的字典。

mydict = {'x': x, 'y': y}
np.save('mydict.npy', mydict)
mydict2 = np.load('mydict.npy', allow_pickle=True)
mydict2
array({'x': <tf.Tensor: id=8, shape=(3,), dtype=float32, numpy=array([1., 1., 1.], dtype=float32)>, 'y': <tf.Tensor: id=9, shape=(4,), dtype=float32, numpy=array([0., 0., 0., 0.], dtype=float32)>},
      dtype=object)

4.5.2 load and save model parameters

我们还可以读写模型的参数。
为了演示方便,我们先创建一个多层感知机,并将其初始化。。

X = tf.random.normal((2,20))
X
<tf.Tensor: id=15, shape=(2, 20), dtype=float32, numpy=
array([[ 1.4838032 , -0.638382  , -0.7836789 ,  1.8679693 , -0.73148364,
        -0.12649764, -0.2709544 , -0.33071974,  0.08754155, -0.11141171,
         0.18274567, -0.64928424, -0.6519136 ,  0.07320689, -0.5973234 ,
         1.9181312 ,  0.47066143, -0.10463867, -0.48717928,  0.3107364 ],
       [ 0.37838233,  0.11170077, -1.3378098 ,  0.3618399 ,  0.27140674,
         0.9901546 ,  1.4799279 ,  1.2373866 , -0.62953895, -1.5107338 ,
        -1.6658096 , -0.08139827,  0.5444429 ,  0.94359463, -0.00676966,
        -1.5311289 , -0.30671307,  0.38309866, -0.2765001 , -0.61528987]],
      dtype=float32)>
class MLP(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.flatten = tf.keras.layers.Flatten()    # Flatten层将除第一维(batch_size)以外的维度展平
        self.dense1 = tf.keras.layers.Dense(units=256, activation=tf.nn.relu)
        self.dense2 = tf.keras.layers.Dense(units=10)

    def call(self, inputs):         
        x = self.flatten(inputs)   
        x = self.dense1(x)    
        output = self.dense2(x)     
        return output

net = MLP()
Y = net(X)
Y
<tf.Tensor: id=71, shape=(2, 10), dtype=float32, numpy=
array([[-0.30077258,  0.4493576 ,  0.00761353, -0.14657806, -0.11702831,
        -0.20244044,  0.15949515, -0.025849  , -0.36856648,  0.23903428],
       [-0.09660852,  0.0096112 ,  0.3435048 , -0.066409  , -0.24335058,
        -0.01852736,  0.77680373, -0.04183513, -0.232623  , -0.5856861 ]],
      dtype=float32)>

下面把该模型的参数存成文件,文件名为4.5saved_model.h5

net.save_weights("4.5saved_model.h5")

接下来,我们再实例化一次定义好的多层感知机。与随机初始化模型参数不同,我们在这里直接读取保存在文件里的参数。

因为这两个实例都有同样的模型参数,那么对同一个输入X的计算结果将会是一样的。我们来验证一下。

net2 = MLP()
net2(X)
net2.load_weights("4.5saved_model.h5")
Y2 = net2(X)
Y2 == Y
<tf.Tensor: id=146, shape=(2, 10), dtype=bool, numpy=
array([[ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True],
       [ True,  True,  True,  True,  True,  True,  True,  True,  True,
         True]])>

注:本节除了代码之外与原书基本相同,原书传送门

4.6 GPU计算

到目前为止,我们一直在使用CPU计算。对复杂的神经网络和大规模的数据来说,使用CPU来计算可能不够高效。在本节中,我们将介绍如何使用单块NVIDIA GPU来计算。首先,需要确保已经安装好了至少一块NVIDIA GPU。然后,下载CUDA并按照提示设置好相应的路径

注意:需要tensorflow-gpu

import tensorflow as tf
import numpy as np
print(tf.__version__)

gpus = tf.config.experimental.list_physical_devices(device_type='GPU')
cpus = tf.config.experimental.list_physical_devices(device_type='CPU')
print("可用的GPU:",gpus,"\n可用的CPU:", cpus)
2.0.0
可用的GPU: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')] 
可用的CPU: [PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU')]

check available device

from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())
[name: "/device:CPU:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: 15592483132577835191
, name: "/device:GPU:0"
device_type: "GPU"
memory_limit: 3063309926
locality {
  bus_id: 1
  links {
  }
}
incarnation: 3859243074925251015
physical_device_desc: "device: 0, name: GeForce GTX 1650, pci bus id: 0000:01:00.0, compute capability: 7.5"
]

specify device

使用tf.device()来指定特定设备(GPU/CPU)

with tf.device('GPU:0'):
    a = tf.constant([1,2,3],dtype=tf.float32)
    b = tf.random.uniform((3,))
    print(tf.exp(a + b) * 2)
    
tf.Tensor([12.172885 19.682476 53.18001 ], shape=(3,), dtype=float32)

注:本节与原书有很大不同,原书传送门

©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页