Tensorflow之基础篇

2020-12-30 16:12:45 浏览数 (1)

这是奔跑的键盘侠的第188篇文章

作者|我是奔跑的键盘侠

来源|奔跑的键盘侠(ID:runningkeyboardhero)

转载请联系授权(微信ID:ctwott)

终于有点时间学一下之前碎碎念的TensorFlow,主要代码为主,内容来源于《简明的TensorFlow2》作者 李锡涵 李卓恒 朱金鹏,人民邮电出版社2020.9第1版。

这本书虽然确实很简明,但是需要一定基础,比如机器学习的基本算法,比如python的基本能力。敲了前面几个简单的神经网络,代码都可以正常运行,不过部分入门代码未注释,如果想系统学习tensorflow的话,还是要多多print(help)或者网上查阅相关资料。

1

关于安装

略……

自行百度

2

常用语法代码

代码语言:javascript复制
## 1.1 TensorFlow 1 1
import tensorflow as tf

#定义一个随机数(标量)
random_float = tf.random.uniform(shape=())
#定义一个有3个元素的零向量
zero_vector = tf.zeros(shape=(3),dtype=tf.int32)

print(random_float)
print(zero_vector)
A = tf.constant([[1.,2.],[3.,4.]])
B = tf.constant([[5.,6.],[7.,8.]])

#张量的重要属性是形状、类型和值,它们分别可以通过张量的shape、dtype属性和numpy()方法获得。
print(A.shape)
print(B.dtype)
print(A.numpy())
#张量的numpy()方法是将张量的值转换为一个Numpy数组。

输出结果:

代码语言:javascript复制
tf.Tensor(0.79740083, shape=(), dtype=float32)
tf.Tensor([0 0 0], shape=(3,), dtype=int32)
(2, 2)
<dtype: 'float32'>
[[1. 2.]
 [3. 4.]]
代码语言:javascript复制
#计算矩阵A与B的和,A与B的乘积。
C = tf.add(A,B)
D = tf.matmul(A,B)

print(C.numpy())
print(D)

输出结果:

代码语言:javascript复制
[[ 6.  8.]
 [10. 12.]]
tf.Tensor(
[[19. 22.]
 [43. 50.]], shape=(2, 2), dtype=float32)
代码语言:javascript复制
## 1.2 自动求导机制
import tensorflow as tf

x = tf.Variable(initial_value=3.)
#x是一个变量,使用tf.Variable()声明,与普通张量一样,该变量同样具有形状、类型和值这3种属性。参数设定初始值为3.0,浮点数。
#变量x与普通张量的区别是,变量默认能够被TensorFlow的自动求导机制求导,so常用于定义机器学习模型的参数。

#tf.GradientTape()是一个自动求导记录器,变量和计算步骤都会被自动记录。
with tf.GradientTape() as tape:
    y = tf.square(x)

# y=x**2被自动记录,可以通过y_grad=tape.gradient(y,x)求张量y对变量x的导数。
y_grad = tape.gradient(y,x)
#计算y关于x的导数
print([y,y_grad])

运行结果:

代码语言:javascript复制
[<tf.Tensor: shape=(), dtype=float32, numpy=9.0>, <tf.Tensor: shape=(), dtype=float32, numpy=6.0>]
代码语言:javascript复制
#同理,我们可以用tf.GradientTape()计算函数L(w,b)=||Xw b-y||**2 在w=[[1],[2]]列向量,b=1时分别对w,b的偏导数,
#其中X=[[1,2],[3,4]]   y=[[1],[2]]:
X = tf.constant([[1.,2.],[3.,4.]])
y = tf.constant([[1.],[2.]])
w = tf.Variable(initial_value=[[1.],[2.]])
b = tf.Variable(initial_value=1.)
with tf.GradientTape() as tape:
    L = tf.reduce_sum(tf.square(tf.matmul(X,w) b-y))
w_grad, b_grad = tape.gradient(L, [w,b])
print(L, w_grad, b_grad)
### tf.squre()用于对输入张量的每一个元素求平方,不改变张量的形状。
tf.reduce_sum()用于对输入张量的所有元素求和。TensorFlow中有大量的张量操作API,包括数学运算、张量形状操作,如tf.reshape(),切片和连接,如tf.concat()等多种类型。可通过查阅TensorFlow官方API文档了解。

运行结果:

代码语言:javascript复制
tf.Tensor(125.0, shape=(), dtype=float32) tf.Tensor(
[[ 70.]
 [100.]], shape=(2, 1), dtype=float32) tf.Tensor(30.0, shape=(), dtype=float32)

3

线性回归

代码语言:javascript复制
## 1.3 TensorFlow下的线性回归

使用tape.gradient(ys,xs)自动计算梯度
使用optimizer.apply_gradients(grads_and_vars)自动更新模型参数。

tf.keras.optimizers.SGD(learning_rate=1e-3)声明了一个梯度下降优化器(optimizer),其学习率为1e-3
优化器可以帮我们根据计算出的求导结果更新模型参数,从而最小化某个特定的损失函数。

而更新模型参数的方法optimizer.apply_gradients()中需要提供参数grads_and_vars,即待更新的变量(variables)和损失函数关于
这些变量的偏导数(如grads)。具体而言,这里需要传入一个Python列表list,list中的每个元素是一个(变量的偏导数,变量)对,比如这里是
[(grad_a,a),(grad_b,b)]。我们通过grads=tape.gradient(loss,variables)求出tape中记录的loss关于variables=[a,b]中每个变量的偏导数,也就是grads=[grad_a,grad_b]再使用python的zip()函数将grads=[grad_a,grad_b]和variables=[a,b]拼装在一起,就可以组合出所需要的参数了。
代码语言:javascript复制
import tensorflow as tf
import numpy as np

X_raw = np.array([2013,2014,2015,2016,2017],dtype=np.float32)
y_raw = np.array([12000,14000,15000,16500,17500],dtype=np.float32)

X = (X_raw - X_raw.min())/(X_raw.max()-X_raw.min())
y = (y_raw - y_raw.min())/(y_raw.max()-y_raw.min())


X = tf.constant(X)
y = tf.constant(y)


a = tf.Variable(initial_value=0.)
b = tf.Variable(initial_value=0.)
# print(X.numpy())
variables = [a,b]

num_epoch = 10000
optimizer = tf.keras.optimizers.SGD(learning_rate=1e-3)
for e in range(num_epoch):
#   使用tf.GradientTape()记录损失函数的梯度信息
    with tf.GradientTape() as tape:
        y_pred = a*X   b
        loss = tf.reduce_sum(tf.square(y_pred - y))
#   TensorFlow自动计算损失函数关于自变量(模型参数)的梯度
    grads = tape.gradient(loss, variables)
#   TensorFlow自动根据梯度更新参数
    optimizer.apply_gradients(grads_and_vars=zip(grads, variables))
    
print(a.numpy(),b.numpy())

运行结果如下:

代码语言:javascript复制
0.9817748 0.0545703
代码语言:javascript复制
代码语言:javascript复制
再补充一段可视化代码:
代码语言:javascript复制
import matplotlib.pyplot as plt
import numpy as np

X_raw = np.array([2013,2014,2015,2016,2017],dtype=np.float32)
y_raw = np.array([12000,14000,15000,16500,17500],dtype=np.float32)
X = (X_raw - X_raw.min())/(X_raw.max()-X_raw.min())
y = (y_raw - y_raw.min())/(y_raw.max()-y_raw.min())
m = X
n = y

plt.scatter(m,n,s=75,alpha=.5)
plt.xlim((0,3))
plt.xticks([0,2,1])#ignore xticks
plt.ylim((0,3))
plt.yticks([0,2,1])#ignore xticks

m = np.linspace(0, 2, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
n = a.numpy()*m   b.numpy()

plt.figure(num=1,figsize=(10,5))
plt.plot(m,n,color='r',linewidth=2,linestyle='--')

plt.show()

4

线性回归的模型玩法

## 上一节的线性回归模型,可以通过该模型实现,代码如下

代码语言:javascript复制
import tensorflow as tf

X = tf.constant([[1.,2.,3.],[4.,5.,6]])
y = tf.constant([[10.],[20.]])

class Linear(tf.keras.Model):
### 继承tf.keras.Model类后,可以使用父类若干方法和属性。
#列入实例化类model = Model()后,可以通过model.variables
#这一属性直接获得模型的所有变量,免去我们一个个显式的指定变量的麻烦。
    def __init__(self):
        super().__init__()
        self.dense =tf.keras.layers.Dense(
            units=1,
            activation=None,
            kernel_initializer=tf.zeros_initializer(),
            bias_initializer=tf.zeros_initializer()
        )
    def call(self,input):
        output = self.dense(input)
        return output
    

#以下是调用代码:
model = Linear()
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
for i in range(100):
    with tf.GradientTape() as tape:
        y_pred = model(X)
        loss = tf.reduce_sum(tf.square(y_pred-y))
    grads = tape.gradient(loss,model.variables)
    
    optimizer.apply_gradients(grads_and_vars=zip(grads,model.variables))
print(model.variables)
# 在这里我们并没有显式的声明a和b两个变量,也没有写出 y_pred=a*X b
# 这一线性变换,而是建立了一个继承了tf.keras.Model模型类Linear。
# 该类在初始化部分实例化了一个全连接层(tf.keras.layers.Dense),
# 并在call()方法中队这个层进行调用,实现了线性变换计算。

运算结果如下:

代码语言:javascript复制
[<tf.Variable 'linear_6/dense_3/kernel:0' shape=(3, 1) dtype=float32, numpy=
array([[0.16730985],
       [1.14391   ],
       [2.1205118 ]], dtype=float32)>, <tf.Variable 'linear_6/dense_3/bias:0' shape=(1,) dtype=float32, numpy=array([0.97660047], dtype=float32)>]

## Keras的全连接层:线性变化 激活函数

#### 全连接层(tf.keras.layers.Dense)是Keras中最基础和常用的层之一,能够对输入矩阵A进行f(AW b)的线性变化 激活函数操作。如果不指定激活函数,就是纯粹的线性变换AW b。具体而言,给定输入张量input=[batch_size,input_dim],该层对输入张量首先进行tf.matmul(input,kernel) bias的线性变化(kernel和bias是层中可训练的变量),然后将线性变换后张量的每个元素通过激活函数activation进行计算,从而输出形状为[batch_size,units]的二维张量。

-END-

© Copyright

奔跑的键盘侠原创作品 | 尽情分享朋友圈 | 转载请联系授权

0 人点赞