第一章 Tensorflow 整体流程和基础语法

注:学习资源源自PKU《人工智能实践-TensorFlow2.0》课程

悄悄:嗷我整理地好辛苦,但是整理的过程强迫我理解每一个知识点~

1.0 神经网络搭建整体流程

整体来看,一个神经网络的搭建需要以下几个步骤:

  1. 准备数据
    (包括:读入,乱序,生成训练集和测试集,配成<特征,标签>对)
  2. 搭建网络
    (定义神经网络中所有可训练参数)
  3. 参数优化
    (嵌套循环迭代,with结构更新参数,显示当前loss)
  4. 测试效果
    (计算当前参数前向传播的准确率,显示当前的acc)
  5. acc和loss可视化(图像展示)

1.1 从栗子引入

介绍:我们要完成搭建神经网络实现鸢尾花分类!

这个例子,使神经网络不那么抽象,从代码的角度实现上述的5个步骤~

TensorFlow提供了很多函数接口,但是也比较难记,我大概都没记住...因此就在代码上面做了标注便于理解,但是还是要先学这章后面的基础知识www!我只是觉得先有个整体概念会学的轻松一些~

实践前准备

我们首先需要对导入的数据集有有所了解,下面用表格的形式来看看数据是什么样子的~

from sklearn import datasets
from pandas import DataFrame
import pandas as pd

# 从sklearn包中直接下载数据集
from sklearn.datasets import load_iris
# 这里返回iris数据集的所有特征
x_data = datasets.load_iris().data
# 这里返回此数据集的所有标签
y_data = datasets.load_iris().target

print("x_data from datasets:\n",x_data)
print("y_data from datasets:\n",y_data)

此时的输出是这样的,乱七八糟一大堆,很不方便

x_data from datasets:

[[5.1 3.5 1.4 0.2]

[4.9 3. 1.4 0.2]

[4.7 3.2 1.3 0.2]

.........

[4.6 3.1 1.5 0.2]

[5. 3.6 1.4 0.2]

因此,我们以表格的形式把数据列出来,并且添加上标签和每个的类别~

# 以表格形式显示数据
x_data = DataFrame(x_data,columns=['花萼长度','花萼宽度','花瓣长度','花瓣宽度'])
pd.set_option('display.unicode.east_asian_width',True) # 设置列名对齐

# 再添加一列,标签为类别
x_data['类别'] = y_data
print("x_data add a column: \n",x_data)

这时的输出就好看多了:

表格形式观察数据集

上面的都不是搭建的过程啊,真正的搭建过程从下面才开始!!

为了弄个完整的,我把导入模块也写一下~
从下面开始的代码就是真正的搭建啦!!

import tensorflow as tf
from sklearn import datasets
from matplotlib import pyplot as plt
import numpy as np

第一步:准备数据!

上面说了这个有很多步骤,包括:读入,乱序,生成训练集和测试集,配成<特征,标签>对。

一步步完成叭~

(1) 数据集读入

这里与前面的数据分析时做表格类似,分别输入输入特征和标签

x_data = datasets.load_iris().data
y_data = datasets.load_iris().target

(2) 数据集乱序

np.random.shuffle(x_data)
np.random.shuffle(y_data)

(3)生成训练集和测试集

分成永不相见的训练集和测试集
训练集为前120行,测试集为后30行
这里的训练集和测试集必须没有交集

x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

而且数据类型转换,否则后面矩阵相乘时会因为数据类型不一致报错!

x_train = tf.cast(x_train,tf.float32)
x_test = tf.cast(x_test,tf.float32)

(4) 配成<特征,标签>对

数据集分批次,每个批次betch组数据

train_db = tf.data.Dataset.from_tensor_slices((x_train,y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test)).batch(32)

第二步:搭建网络!

定义神经网络中所有可训练的参数
这里由于输入4个特征,因此输入层有四个输入节点
分为3类,因此有3个神经元。

w = tf.Variable(tf.random.truncated_normal([4,3],stddev=0.1,seed=1))
b = tf.Variable(tf.random.truncated_normal([3],stddev=0.1,seed=1))

(这步还蛮简单的,因为还没涉及到复杂的网络,这里也是以后的章节要学习的部分!)

第三四步:参数优化的训练和测试!

因为每一次参数优化后,我们都希望去将改变进行测试,所以训练和测试是在一个for循环里面的!所以就放在一步说哦~

先定义参数和画图用的存储的空列表

lr = 0.1 # 学习率为0.1
train_loss_result = []  # 每轮的loss记录在这里,画图用
test_acc_result = []  # 每轮的acc记录在这里,画图用
epoch = 500  # 循环迭代500轮
loss_all = 0  # 每轮4个step,loss_all记录4个loss的和

训练部分和测试部分

# 每个epoch循环一个数据集
for epoch in range(epoch):
  # betch级别的循环
  for step,(x_train,y_train) in enumerate(train_db):
    # with结构记录梯度信息
    with tf.GradientTape() as tape:
      # 神经网络的y = w * x + b
      y = tf.matmul(x_train,w) + b
      # 使输出的y符合概率分布
      y = tf.nn.softmax(y)
      # 转换为独热码格式
      y_ = tf.one_hot(y_train,depth = 3)
      # 均方差误差损失函数
      loss = tf.reduce_mean(tf.square(y_ - y))
      # 将计算出的loss累加
      loss_all += loss.numpy()
    # 求导,计算loss对各个参数的梯度
    grads = tape.gradient(loss,[w,b])
    # 两个参数的自更新
    w.assign_sub(lr * grads[0])
    b.assign_sub(lr * grads[1])
  

  # 测试部分
  total_correct,total_number = 0,0
  for x_test,y_test in test_db:
    # 使用更新后的参数进行预测
    y = tf.matmul(x_test,w) + b
    y = tf.nn.softmax(y)  # 概率分布
    # 返回y中最大值的索引,也就是分类的结果
    pred = tf.argmax(y,axis=1)
    pred = tf.cast(pred,dtype=y_test.dtype)
    # 布尔型转化为整形:true->1,false->0
    correct = tf.cast(tf.equal(pred,y_test),dtype=tf.int32)
    correct = tf.reduce_sum(correct)
    total_correct += int(correct) # 每个betch的加起来
    total_number += x_test.shape[0] # 将所有betch的加起来
  
  acc = total_correct / total_number

  # 每个epoch,打印loss信息和acc信息
  print("Epoch {}, loss {},\t acc {}".format(epoch,loss_all / 4,acc))

  # 同步在要画图的里面
  train_loss_result.append(loss_all / 4)
  test_acc_result.append(acc)

  # 归零
  loss_all = 0

上面这个好重要!!!几乎每个的训练和测试都和这个类似~

结果是这样的:

这个是一个片段,但是也可以看出loss在下降,acc在上升~说明w和b两个参数在优化呐!

优化过程

第五步:acc和loss可视化(图像展示)

因为之前以及将loss 和 acc 每一次的变化值都存在train_loss_result和test_acc_result两个数组里了,所以直接画就行~一般都画在两个表里,我这样画在一个有点丑==。

plt.title("Result: Loss & Acc")
plt.xlabel('Epoch')
plt.ylabel('Acc')
plt.ylabel('Loss')
plt.plot(test_acc_result,label = "$Accuracy$")

plt.plot(train_loss_result,label = "$Loss$")
plt.legend()
plt.show()

可视化结果

到这里就可以直观地看到神经网络的优化过程啦,这里最后的准确率是100%,因为特征值很少,以后会学到很多有意思的!也会在第二步:搭建网络做很多学习~(我猜的,学了不对再说..hhh)

1.2 基础知识

1.2.1 张量的生成

张量:多维数组(列表)

维数n,阶n,例如:t = [[[..[[ (共n个)

数据类型

  1. tf.int,tf.float (tf.int32,tf.float64...)
  2. tf.bool
    例如:tf.constant([True,False])
  3. tf.string
    例如:tf.constant("Hello World!")

代码实现

# 创建一个张量
a = tf.constant([1,5],dtype=tf.int64)
print(a)  

结果:
tf.Tensor([1 5], shape=(2,), dtype=int64)
看shape中逗号隔开了几个数字,就是几维的。(这里是一维的)
这里的"2"表示有几个元素,也就是前面的1和5。

# 当给出的为numpy形式的数据
import numpy as np
a = np.arange(0,5)
b = tf.convert_to_tensor(a,dtype=tf.int64)
print(a)
print(b)

结果:[0 1 2 3 4]
tf.Tensor([0 1 2 3 4], shape=(5,), dtype=int64)

# 创建全为0的张量(维度)
a = tf.zeros([2,3])
# 创建全为1的张量(维度)
b = tf.ones(4)
# 创建全为指定值的张量(维度,指定值)
c = tf.fill([2,2],9)

print(a)
print(b)
print(c)

结果:
tf.Tensor(
[[0. 0. 0.]
[0. 0. 0.]], shape=(2, 3), dtype=float32)
tf.Tensor([1. 1. 1. 1.], shape=(4,), dtype=float32)
tf.Tensor(
[[9 9]
[9 9]], shape=(2, 2), dtype=int32)

# 创建随机正态分布的随机数张量
# 这里由以0.5为均值,1为标准差的随机数组成
d = tf.random.normal([2,2],mean = 0.5,stddev=1)
# 生成截断式正态分布的随机数
e = tf.random.truncated_normal([2,2],mean = 0.5,stddev=1)

print(d)
print(e)

结果:
tf.Tensor(
[[-0.42571473 -0.37063456]
[-1.0308502 -0.30630374]], shape=(2, 2), dtype=float32)
tf.Tensor(
[[1.7382374 1.1409175]
[1.937354 1.7825055]], shape=(2, 2), dtype=float32)

# 生成均匀分布的随机数
f = tf.random.uniform([2,2],minval=0,maxval=1)  # 给定随机数区域:[minval,maxval)
print(f)

结果:
tf.Tensor(
[[0.73435974 0.33123863]
[0.69566035 0.6909727 ]], shape=(2, 2), dtype=float32)

1.2.2常用函数

代码实现

# 创造数据
x1 = tf.constant([1.,2.,3.],dtype=tf.float64)
print(x1)

结果:
tf.Tensor([1. 2. 3.], shape=(3,), dtype=float64)

# 强制转换数据类型
x2 = tf.cast(x1,tf.int32)
print(x2)

结果:
tf.Tensor([1 2 3], shape=(3,), dtype=int32)

# 计算张量维度上的最大最小值
print(tf.reduce_min(x2))  # min
print(tf.reduce_max(x2))  # max

结果:
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)

x = tf.constant([[1,2,3],[2,2,3]])
print(x)
# 计算张量沿指定维度的平均值(若不指定为所有元素)
print(tf.reduce_mean(x)) # 所有元素均值
print(tf.reduce_mean(x,axis=0)) # axis为0是纵向

# 计算张量沿指定维度的和(若不指定为所有元素)
print(tf.reduce_sum(x))
print(tf.reduce_sum(x,axis = 1))  # axis = 1为横向

结果:
tf.Tensor(
[[1 2 3]
[2 2 3]], shape=(2, 3), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tf.Tensor(13, shape=(), dtype=int32)
tf.Tensor([6 7], shape=(2,), dtype=int32)

# 将变量标记为可训练
# 这里为初始化代码:生成随机正太分布的数,并记作可训练
w = tf.Variable(tf.random.normal([2,2],mean = 0,stddev=1))
# 对应元素的四则运算(只有维度相同才可以进行四则运算)
a = tf.ones([1,3])
b = tf.fill([1,3],3.)
print(a)
print(b)
print(tf.add(a,b))
print(tf.subtract(a,b))
print(tf.multiply(a,b))
print(tf.divide(a,b))

结果:
tf.Tensor([[1. 1. 1.]], shape=(1, 3), dtype=float32)
tf.Tensor([[3. 3. 3.]], shape=(1, 3), dtype=float32)
tf.Tensor([[4. 4. 4.]], shape=(1, 3), dtype=float32)
tf.Tensor([[-2. -2. -2.]], shape=(1, 3), dtype=float32)
tf.Tensor([[3. 3. 3.]], shape=(1, 3), dtype=float32)
tf.Tensor([[0.33333334 0.33333334 0.33333334]], shape=(1, 3), dtype=float32)

# 平方,N次方,开方
a = tf.fill([1,2],3.)
print(a)
print(tf.square(a))
print(tf.pow(a,3))
print(tf.sqrt(a))

结果:
tf.Tensor([[3. 3.]], shape=(1, 2), dtype=float32)
tf.Tensor([[9. 9.]], shape=(1, 2), dtype=float32)
tf.Tensor([[27. 27.]], shape=(1, 2), dtype=float32)
tf.Tensor([[1.7320508 1.7320508]], shape=(1, 2), dtype=float32)

# 矩阵乘法(n,m) * (m,k)
a = tf.ones([3,2])
b = tf.fill([2,3],3.)
print(tf.matmul(a,b))

结果:
tf.Tensor(
[[6. 6. 6.]
[6. 6. 6.]
[6. 6. 6.]], shape=(3, 3), dtype=float32)

# 将特征和标签配对
features = tf.constant([12,23,10,17])
labels = tf.constant([0,1,1,0])
dataset = tf.data.Dataset.from_tensor_slices((features,labels))
print(dataset)
for element in dataset:
  print(element)

结果:
<TensorSliceDataset shapes: ((), ()), types: (tf.int32, tf.int32)>
(<tf.Tensor: shape=(), dtype=int32, numpy=12>, <tf.Tensor: shape=(), dtype=int32, numpy=0>)
(<tf.Tensor: shape=(), dtype=int32, numpy=23>, <tf.Tensor: shape=(), dtype=int32, numpy=1>)
(<tf.Tensor: shape=(), dtype=int32, numpy=10>, <tf.Tensor: shape=(), dtype=int32, numpy=1>)
(<tf.Tensor: shape=(), dtype=int32, numpy=17>, <tf.Tensor: shape=(), dtype=int32, numpy=0>)

# with结构记录计算过程,gradient求出张量的梯度
with tf.GradientTape() as tape:
  w = tf.Variable(tf.constant(3.0))
  loss = tf.pow(w,2)
grad = tape.gradient(loss,w)
print(grad)

结果:
tf.Tensor(6.0, shape=(), dtype=float32)

# 遍历所有元素并配上索引号
seq = ['one','two','three']
for i,element in enumerate(seq):
  print(i,element)

结果:
0 one
1 two
2 three

# 转换成独热码
classes = 3
labels = tf.constant([1,0,2])
output = tf.one_hot(labels,classes)
print(output)

结果:
tf.Tensor(
[[0. 1. 0.]
[1. 0. 0.]
[0. 0. 1.]], shape=(3, 3), dtype=float32)

# 使n个分类的n个输出符合概率分布
y = tf.constant([1.01,2.05,-0.66])
y_pro = tf.nn.softmax(y)
print("After softmax, y_pro is :",y_pro)

结果:
After softmax, y_pro is : tf.Tensor([0.24891327 0.7042296 0.04685719], shape=(3,), dtype=float32)

# 自更新函数
w = tf.Variable(4)
w.assign_sub(1)
print(w)
w.assign_add(5)
print(w)

结果:
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=3>
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=8>

# 返回张量沿着指定维数最值的索引号
test = np.array([[1,2,3],[2,3,4],[5,4,3],[8,7,2]])
print(test)
print(tf.argmax(test,axis = 0)) # 返回每一列最大值的索引 
print(tf.argmax(test,axis = 1)) # 返回每一行最大值的索引

结果:
[[1 2 3]
[2 3 4]
[5 4 3]
[8 7 2]]
tf.Tensor([3 3 1], shape=(3,), dtype=int64)
tf.Tensor([2 2 0 0], shape=(4,), dtype=int64)

1.2.3 优化思想

在神经网络的设计过程中,我们需要通过一次次的迭代来更新参数使得算式准确!

目的:优化参数

介绍:
MP 模型 : 每个输入特征乘以线上的权重,求和,再通过非线性函数输出。

搭建全连接网络

神经网络执行前向传输--》 y = x * w + b;
(x:输入特征,w先随机初始化,b也随机初始化,y:预测值)

由于是随机产生的,因此此时的结果是蒙的。需要引入损失函数

损失函数---》预测值和标准答案的优劣。
当损失函数输出最小时,参数w和b(原为随机)就会是最优值

反向传播:从后向前,逐层求损失函数对每层神经元参数的偏导,迭代更新所有参数。

反向传播公式:

(它不识别我的公式编辑语法哼!我昨天就因为查怎么写公式还耽误了CF十多分钟呜呜呜)

反向传播公式

这里假设损失函数为(w + 1) ^ 2

import tensorflow as tf

# 随机初始值为5
w = tf.Variable(tf.constant(5,dtype=tf.float32))
# 学习率
lr = 0.1
# 循环迭代
epoch = 40

# 定义顶层循环,表示对数据集循环epoch次
for epoch in range(epoch):
  # 梯度的计算过程
  with tf.GradientTape() as tape:
    loss = tf.square(w + 1) # 损失函数
  grades = tape.gradient(loss,w) # 让损失函数loss对参数w求梯度

  w.assign_sub(lr * grades) # 自减函数相当于 w -= (lr * grades)
  print("After %s epoch, w is %f, loss is %f"%(epoch,w.numpy(),loss))

结果

可以看到loss在减小,w逐渐接近-1,也就是极值点!

啊!学完第一章可以了解整体的框架,老师讲的好棒!!时间很短但是都是干货!

这些代码我敲了研究了好久才懂,脑子不好用哎,不过结果不错!这个笔记写的花费了不少时间,但是我觉得很值得,本来计划学完就去玩~ 我相信这个笔记以后看肯定也会有收获哒!既然我觉得写了会有用,那我开开心心写就是了!

Last modification:July 25th, 2020 at 10:22 pm
请赏我杯奶茶,让我快乐长肉