关于axis轴参数理解

坐标轴

假定是二维元素

就是行元素和列元素变化的方向

axis=1对列进行操作,axis=0对行进行操作

tf.Tensor

  • list
  • np.array
  • tf.Tensor

为了实现gpu加速,所以重新开发了科学计算库


基本类型

  • scalar: 1.1
  • vector:[1.1,2.2,3.3]
  • matrix:[[1.1,2.2],[3.3,4.4]]
  • Tensor:dim>2

在 TensorFlow 中间,为了表达方便,一般把标量、向量、矩阵也统称为张量,不作区分,需要根据张量的维度数或形状自行判断


  • int,float,double

    int32,int64,float32,float64

  • bool

  • string


tf.constant()

用来创建一个标量

可以用以下方法:

  • .gpu( ) :使用gpu
  • .cpu( ) :使用cpu
  • .numpy( ) :返回当前值的numpy的数据类型
  • .nidm : 返回维度
  • .shape :返回形状

tf.rank( ) :

可以用来查看张量对象的所有信息

tf.is_tensor( ):

用来判断是否是tf.tensor类型

tf.convert_to_tensor( ):

可以把numpy类型转化为tensor类型

tf.cast(x,dtype=)

可以转化类型的精度

tf.Variable

创建一个待优化张量

由于梯度运算会消耗大量的计算资源,而且会自动更新相关参数,对于不需要的优化的张量,如神经网络的输入𝑿,不需要通过tf.Variable 封装;相反,对于需要计算梯度并优化的张量,如神经网络层的𝑾和𝒃,需要通过tf.Variable 包裹以便TensorFlow 跟踪相关梯度信息。

比起普通张量多了两个属性【对象属性】:

  • name

    name 属性用于命名计算图中的变量,这套命名体系是TensorFlow 内部维护的,一般不需要用户关注name 属性

  • trainable

    trainable属性表征当前张量是否需要被优化,创建Variable 对象时是默认启用优化标志,可以设置
    trainable=False 来设置张量不需要优化。


然而普通张量其实也可以通过GradientTape.watch()方法临时加入跟踪梯度信息的列表,从而支持自动求导功能。

创建一个tensor

转换numpy类型

tf.constant( ) 或 tf.Varible( ) 或 tf.convert_to_tensor( )

可以使用这三个函数来转化numpy类型来创建tensor

1
2
3
4
5
6
7
8
tf.convert_to_tensor(np.ones([2,3]),dtype=tf.float32)
#<tf.Tensor: id=11, shape=(2, 3), dtype=float32, numpy=
#array([[1., 1., 1.],
# [1., 1., 1.]], dtype=float32)>
tf.Variable(np.ones([2,3]))
#<tf.Variable 'Variable:0' shape=(2, 3) dtype=float64, numpy=
#array([[1., 1., 1.],
# [1., 1., 1.]])>

全部初始化为指定值

tf.zeros( )

创建全0张量

tf.zeros([ ])

传入[1,2]类似np.zeros((1,2))或np.zeros([1,2])

  • tf.zeros_like( )

    与np.zeros_like( )类似


tf.ones( )

创建全1 张量

使用方法同上

  • tf.ones_like( )

    使用方法同上


tf.fill(shape,value)

除了初始化为全0,或全1 的张量之外,有时也需要全部初始化为某个自定义数值的
张量,比如将张量的数值全部初始化为−1等。

1
2
3
4
tf.fill([2,2],3)
#<tf.Tensor: id=13, shape=(2, 2), dtype=int32, numpy=
#array([[3, 3],
# [3, 3]])>

创建已知分布的张量

正态分布

tf.random.normal(shape, mean=0.0, stddev=1.0)

可以创建形状为shape,均值为mean,标准差为stddev 的正态分布𝒩($mean, stddev^2$)。

  • tf.random.truncated_normal(shape, mean=0.0, stddev=1.0)

    会截取正态分布的一部分,有利于sigmoid函数的梯度下降

1
2
3
4
5
>>> tf.random.truncated_normal([3,3], mean=0.0, stddev=1.0)
<tf.Tensor: id=25, shape=(3, 3), dtype=float32, numpy=
array([[ 0.07241093, -0.9492289 , 1.1106242 ],
[-1.0468991 , -0.00725726, 1.6111004 ],
[-1.2311684 , 1.380989 , 0.62593746]], dtype=float32)>

均匀分布

tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)

可以创建采样自[minval, maxval)区间的均匀分布的张量。

  • 如果需要均匀采样整形类型的数据,必须指定采样区间的最大值maxval 参数,同时指定数据类型为tf.int*型
1
2
3
4
5
tf.random.uniform([3,3],minval=1,maxval=100)
#<tf.Tensor: id=32, shape=(3, 3), dtype=float32, numpy=
#array([[77.254425, 22.276836, 78.80216 ],
# [68.57904 , 3.394451, 20.12668 ],
# [93.60507 , 49.33812 , 62.72094 ]], dtype=float32)>

创造序列

tf.range(limit,delta=1)

tf.range(limit, delta=1)可以创建[0, limit)之间,步长为delta 的整型序列,不包含limit 本身。


Scalar:

  • [ ]
  • loss
  • accuracy

Vector:

  • Bias
  • [out_dim]

Matrix:

  • input x:[b,vec_dim]
  • weight:[input_dim,output_dim]

Dim=3 Tensor:

  • x: [b,seq_len,word_dim]

一般将单词通过嵌入层(Embedding Layer)编码为固定长度的向量,比如“a”编码为某个长度3 的向量,那么2 个等长(单词数量为5)的句子序列可以表示为shape 为[2,5,3]的3 维张量,其中2 表示句子个数,5 表示单词数量,3 表示单词向量的长度。

Dim=4 Tensor

  • Image:[b,h,w,3]
  • feature maps:[b,h,w,c]

图片相关的处理

Dim=5 Tensor

  • Single task: [b,h,w,3]
  • meta-learning: [task_b,b,h,w,3]

索引与切片

Basic indexing and Numpy-style indexing

在 TensorFlow 中,支持基本的[𝑖],[𝑗] ⋯标准索引方式,也支持通过逗号分隔索引号的索引方式。

推荐用x[1,2,3]这种索引方式

1
2
3
4
5
x=tf.random.normal([1,2,3,4],mean=0,stddev=1)
x[0]
x[0][1]
x[0][1][2]
x[0,1,2]

start : end :step

与numpy中使用方式相同

为了避免出现像 [: , : , : ,1]这样过多冒号的情况,可以使用⋯符号表示取多个维度上所有的数据,其中维度的数量需根据规则自动推断:当切片方式出现⋯符号时,⋯符号左边的维度将自动对齐到最左边,⋯符号右边的维度将自动对齐到最右边,此时系统再自动推断⋯符号代表的维度数量

  • 读取第 1~2 张图片的G/B 通道数据,实现如下:

    1
    x[0:2,...,1:]

特别地,step 可以为负数,考虑最特殊的一种例子,当step = −1时,start: end: −1表示从start 开始,逆序读取至end 结束(不包含end),索引号𝑒𝑛𝑑 ≤ 𝑠𝑡𝑎𝑟𝑡。考虑一个0~9 的简单序列向量,逆序取到第1 号元素,不包含第1 号

Selective Indexing

  • tf.gather(params,indices,axis=0)

    从params的axis维根据indices的参数值顺序获取切片

    只在一个维度中取值

  • tf.gather_nd( params, indices, name=None )

    这里的idices可以为[[0, 2], [0, 4], [2, 2]]或[[0], [1]]或([0])或[0, 1]

    类似坐标轴取数

    有一点不同的地方是[[2,3]]和[2,3]一个返回向量,一个返回标量

  • tf.boolean_mask( tensor, mask, name=’boolean_mask’, axis=None )

    根据bool类型mask数组来判断是非保留数据

    1
    2
    3
    4
    5
    import tensorflow as tf
    a=tf.range(5*5*5*5)
    a=tf.reshape(a,[5,5,5,5])
    tf.boolean_mask(a,mask=[True,True,False,False,True])#只取0,1,4号第0轴元素
    tf.boolean_mask(a,mask=[True,True,False,False,True],axis=3)#只取第3轴的0,1,4号

维度变换

全连接层的向前传播:

在tensorflow中:$Y=X@W+b$


其实都差不多

tf.reshape(x, new_shape)

通过tf.reshape(x, new_shape),可以将张量的视图任意地合法改变

tf.expand_dims(x,axis) 和 tf.squeeze(x,axis)

tf.expand_dims(x, axis)可在指定的axis 轴前可以插入一个新的维度

需要注意的是,tf.expand_dims 的axis 为正时,表示在当前维度之前插入一个新维度;为
负时,表示当前维度之后插入一个新的维度。

tf.squeeze(x,axis)可删除指定轴

tf.transpose(x,perm)

perm提供新的轴的读取顺序


广播

tf.broadcast_to( input, shape, name=None )

几乎不会用到,tensorflow里有与numpy类似的广播机制

将原始矩阵成倍增加

基本运算

Outline

  • +-*/

  • ** , pow ,square

  • sqrt

  • //,%

  • exp,log

    tf.math.log #只以e为底

    其他底只能通过对数相除得到

    tf.exp

    tf2.2 exp已经被移到math模块

  • @,matmul

    类似np.dot

  • linear layer


Operation type

  • element-wise(对应元素运算)

    • +-*/
  • matrix-wise

    • @,matmul
  • dim-wise
    • reduce_mean/max/min/sum

前向传播实战

与python+numpy实现方式类似,但tensorflow提供了自动求导函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
# coding: utf-8

# In[1]:


import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets
import numpy as np
import matplotlib.pyplot as plt


# - 可以使用以下方法屏蔽tensorflow无关信息

# In[2]:


import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'


# - 现在加载文件

# In[3]:


(x,y),_=datasets.mnist.load_data( )


# In[4]:


x=tf.constant(x,dtype=tf.float32)/255
y=tf.constant(y,dtype=tf.int32)


# - 现在对数据进行切片并打包

# In[5]:


train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128)
loss_data=[]


# - 对权重进行初始化

# In[6]:


w1=tf.Variable(tf.random.truncated_normal([784,256],stddev=0.1))
b1=tf.Variable(tf.zeros([256]))

w2=tf.Variable(tf.random.truncated_normal([256,128],stddev=0.1))
b2=tf.Variable(tf.zeros([128]))

w3=tf.Variable(tf.random.truncated_normal([128,10],stddev=0.1))
b3=tf.Variable(tf.zeros([10]))

lr=1e-3


# - 向前传播

# In[7]:


for epoch in range(30):
for step,(x,y) in enumerate(train_db):
x=tf.reshape(x,[-1,28*28])

with tf.GradientTape() as tape:
h1= x@w1 + b1
h1=tf.nn.relu(h1)

h2= h1@w2 + b2
h2=tf.nn.relu(h2)

out= h2@w3 +b3

y_onehot=tf.one_hot(y,depth=10)

loss=tf.square(y_onehot - out)
loss=tf.reduce_mean(loss)

grads=tape.gradient(loss,[w1,b1,w2,b2,w3,b3])

w1.assign_sub(lr*grads[0])
b1.assign_sub(lr*grads[1])
w2.assign_sub(lr*grads[2])
b2.assign_sub(lr*grads[3])
w3.assign_sub(lr*grads[4])
b3.assign_sub(lr*grads[5])

if step % 100 == 0:
print(epoch,step,'loss',float(loss))
loss_data.append(loss)


# In[8]:


plt.plot(loss_data)
plt.show()

有几个注意事项:

  • tf.GradientTape( )默认只监控由tf.Variable创建的traiable=True属性(默认)的变量:

    常用语句:

    • with tf.GradientTape( ) as tipe:

    • tipe.gradient(f,[value])

      对f( value )求导

  • tf.data.Dataset.from_tensor_slices((x,y)).batch(128)

    • tf.data.Dataset.from_tensor_slices( )会对最外层分割成单独元素,第0轴
    • .batch( )方法可以重新将单独元素打包
  • .assign_sub( )方法可以实现tf.Variable创建的变量的自我更新

    因为如果使用w=w-lr*grads , 经过减法运算后的返回值为tf.tensor , 不为tf.Variable ,会影响下一次循环

进阶操作

数据打乱

tf.random.shuffle(x)

将x中元素顺序打乱

合并与分割

合并

tf.concat([,],axis)

按对应轴合并数据

1
2
3
4
5
6
import tensorflow as tf
a=tf.ones([2,35,8])
b=tf.ones([3,35,8])
c=tf.concat([a,b],axis=0)
c.shape
#TensorShape([5, 35, 8])

tf.stack([,],axis)

可在指定的axis 轴前可以插入一个新的维度,并合并传入的数据

但对合并数据shape有要求,必须保持一致的shape

分割

tf.unstack([,],axis)

可以按轴拆开数据

tf.split(x,num_or_size_splits,axis)

  • x 参数:待分割张量。

  • num_or_size_splits 参数:切割方案。当num_or_size_splits 为单个数值时,如10,表
    示等长切割为10 份;当num_or_size_splits 为List 时,List 的每个元素表示每份的长
    度,如[2,4,2,2]表示切割为4 份,每份的长度依次是2、4、2、2。

  • axis 参数:指定分割的维度索引号。

数据统计

向量范数

tf.norm(x,ord,axis)

在 TensorFlow 中,可以通过tf.norm(x, ord)求解张量的L1、L2、∞等范数,其中参数
ord 指定为1、2 时计算L1、L2 范数,指定为np.inf 时计算∞ −范数。

最值,均值,和

以下函数在没指定axis的值时,默认在所有维度进行判断

tf.reduce_max(x,axis)

求最大值

tf.reduce_min(x,axis)

求最小值

tf.reduce_mean(x,axis)

求均值

tf.reduce_sum(x,axis)

求和

tf.argmax(x,axis) 和 tf.argmin(x,axis)

与numpy中使用方式一致,返回索引

张量比较

tf.equal(a,b) 和 tf.math.equal(a,b)

可以将预测值与真实值比较,返回布尔类型的张量

tf.unique(x)

返回x的不充分表 和 原来表与不重复表中的索引

1
2
3
4
a=tf.constant([3,4,4,1,1,1,0])
tf.unique(a)
#Unique(y=<tf.Tensor: id=22, shape=(4,), dtype=int32, numpy=array([3, 4, 1, 0])>,
#idx=<tf.Tensor: id=23, shape=(7,), dtype=int32, numpy=array([0, 1, 1, 2, 2, 2, 3])>)

可以使用tf.gather(Unique,idx)完成逆运算

张量排序

Outline:

  • sort/argsort
  • Topk
  • Top-5 Acc.

tf.sort(x,direction=’ASCENDING’) 和 tf.argsort(x,direction=’ASCENDING’)

tf.sort( )按照升序或者降序对张量进行排序
tf.argsort( )按照升序或者降序对张量进行排序,但返回的是索引

direction=’DESCENDING’ 时为降序

direction=’ASCENDING’ 时为升序

tf.math.top_k(x,k)

tf.math.top_k(x,k)返回带有前k个最大值的对象

如a=tf.math.top_k(x,k)

  • a.value为带有前k个最大值的张量
  • a.indices为带有前k个最大值的索引的张量

应用:

  • top-k accuracy:
    • Prob:[0.1,0.2,0.3,0.4]
    • Label:[2]

Only consider top-1 prediction: [3] 0%

Only consider top-2 prediction: [3,2] 100%

Only consider top-3 prediction: [3,2,1] 100%

一般会考虑top-5 的预测值去判断模型的性能好坏

Top-k Accuracy: 【仅限二维】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def accuracy(output,target,topk=(1,)):
maxk=max(topk)
batch_size=target.shape[0]

pred=tf.math.top_k(output,maxk).indices
pred=tf.transpose(pred,[1,0])

target_=tf.broadcast_to(target,pred.shape)
correct=tf.equal(pred,target_)

res=[ ]
for k in topk:
correct_k=tf.cast(tf.reshape(correct[:k],[-1]),dtype=tf.float32)
correct_k=tf.reduce_sum(correct_k)
acc=float(correct_k/batch_size)

return res

accuracy(output,target,topk=(1,2,3,4,5))

填充与复制

Outline

  • pad
  • tile
  • broadcast_to

tf.pad( tensor,paddings,mode=’CONSTANT’,name=None )

  • tensor是待填充的张量
  • paddings指出要给tensor的哪个维度进行填充,以及填充方式,要注意的是paddings的rank必须和tensor的rank相同
  • mode指出用什么进行填充,’CONSTANT’表示用0进行填充(总共有三种填充方式,本文用CONSTANT予以说明pad函数功能)
  • name就是这个节点的名字

padding=([[ , ],[ , ],[ , ]])

padding的输入大概为这种格式,一个列表表示一个维度的上下左右填充数目且一个列表只有两个元素,表示作用

1
2
3
4
5
6
7
8
9
a=tf.ones([3,3])
tf.pad(a,[[1,2],[3,4]])
# <tf.Tensor: id=25, shape=(6, 10), dtype=float32, numpy=
#array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
# [0., 0., 0., 1., 1., 1., 0., 0., 0., 0.],
# [0., 0., 0., 1., 1., 1., 0., 0., 0., 0.],
# [0., 0., 0., 1., 1., 1., 0., 0., 0., 0.],
# [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
# [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>

tf.tile(tensor,[ , ,])

[ , , ]在里面填每个维度有多少的基础单元tensor,因为必有一个基础单元,所以不能填0

1
2
3
4
5
6
7
a=tf.constant([[1,2,3],[3,4,5]])
tf.tile(a,[2,3])
#<tf.Tensor: id=36, shape=(4, 9), dtype=int32, numpy=
#array([[1, 2, 3, 1, 2, 3, 1, 2, 3],
# [3, 4, 5, 3, 4, 5, 3, 4, 5],
# [1, 2, 3, 1, 2, 3, 1, 2, 3],
# [3, 4, 5, 3, 4, 5, 3, 4, 5]])>

tf.broadcast_to(tensor,[ , , ])

把tensor尽可能填充成shape[ , , ]

张量限幅

tf.maximum(tensor,x) 和 tf.minimum(tensor,x)

用法同numpy

tf.clip_by_value(a,min,max)

用法相当于minimum(max,maximum(min,a))

将数据输出限定到一个闭区间内

tf.clip_by_norm(a,number)

将向量先变成单位向量再乘以number的值,相当于改变向量的模长,不会改变梯度的方向

tf.clip_by_global_norm(grads,number)

new_grads,total_norm = tf.clip_by_global_norm(grads,number)

梯度裁断(Gradient Clipping)有利于解决梯度爆炸和梯度消失

高阶操作技巧

Outline:

  • Where
  • scatter_nd
  • mashgrid

tf.where

通过 tf.where(cond, a, b)操作可以根据cond 条件的真假从参数𝑨或𝑩中读取数据,条件
判定规则如下:

其中𝑖为张量的元素索引,返回的张量大小与𝑨和𝑩一致,当对应位置的cond𝑖为True,𝑜𝑖从
𝑎𝑖中复制数据;当对应位置的cond𝑖为False,𝑜𝑖从𝑏𝑖中复制数据。


但是只传入一个参数时,如tf.where(tensor),会返回非零元素的索引值所构成的tensor

tf.scatter_nd(indices,updates,shape)

通过 tf.scatter_nd(indices, updates, shape)函数可以高效地刷新张量的部分数据,但是这
个函数只能在全0 的白板张量上面执行刷新操作,因此可能需要结合其它操作来实现现有
张量的数据刷新功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
In [62]: # 构造写入位置,即2 个位置
indices = tf.constant([[1],[3]])
updates = tf.constant([# 构造写入数据,即2 个矩阵
[[5,5,5,5],[6,6,6,6],[7,7,7,7],[8,8,8,8]],
[[1,1,1,1],[2,2,2,2],[3,3,3,3],[4,4,4,4]]
])
# 在shape 为[4,4,4]白板上根据indices 写入updates
tf.scatter_nd(indices,updates,[4,4,4])
#Out[62]:<tf.Tensor: id=477, shape=(4, 4, 4), dtype=int32, numpy=
#array([[[0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0]],
# [[5, 5, 5, 5], # 写入的新数据1
# [6, 6, 6, 6],
# [7, 7, 7, 7],
# [8, 8, 8, 8]],
# [[0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0],
# [0, 0, 0, 0]],
# [[1, 1, 1, 1], # 写入的新数据2
# [2, 2, 2, 2],
# [3, 3, 3, 3],
# [4, 4, 4, 4]]])>

tf.meshgrid(x,y)

与matlab里的meshgrid用途和操作一致

数据加载(tenserflow预设数据集)

Outlline

  • keras.datasets
    • boston housing
    • mnist/fashion-MNIST dataset
    • cifar10/100
    • imdb
  • tf.data.Dataset.from_tensor_slices
    • shuffle
    • map
    • batch
    • repeat

常用函数

(x,y),(x_test,y_test)=tf.keras.datasets.mnist.load_data( )

tf.one_hot(tensor,depth= )


db=tf.data.Dataset.from_tensor_slices((x_test,y_test))

  • .shuffle(number)方法

    打乱顺序

  • .map( )方法

    数据预处理

    1
    2
    3
    4
    5
    6
    def prepocess(x,y):
    x=tf.cast(x,dtype=tf.float32)/255
    y=tf.cast(y,dtype=tf.int64)/255
    y=tf.one_hot(y,depth=10)
    return x,y
    db2=db.map(prepocess)
  • .batch( )方法

  • .repeat( )方法

    1
    2
    3
    db4=db.repeat(30)
    for x,y in db4:
    pass

    数据加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def prepocess(x,y):
x=tf.cast(x,dtype=tf.float32)/255
y=tf.cast(y,dtype=tf.int64)/255
y=tf.one_hot(y,depth=10)
return x,y

def mnist_dataset():
(x,y),(x_val,y_val)=datasets.fashion_mnist.load_data()

ds=tf.data.Dataset.from_tensor_slices((x,y))
ds=ds.map(prepocess)
ds=ds.shuffle(60000).batch(100)

ds_val=tf.data.Dataset.from_tensor_slices((x_val,y-val))
ds_val=ds_val.map(prepocess)
ds_val=ds_val.shuffle(10000).batch(100)

return ds,ds_val

测试张量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/usr/bin/env python
# coding: utf-8

# In[1]:


import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets
import numpy as np
import matplotlib.pyplot as plt


# - 可以使用以下方法屏蔽tensorflow无关信息

# In[2]:


import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'


# - 现在加载文件

# In[3]:


(x,y),(x_test,y_test)=datasets.mnist.load_data( )


# In[4]:


x=tf.constant(x,dtype=tf.float32)/255
y=tf.constant(y,dtype=tf.int32)
x_test=tf.constant(x_test,dtype=tf.float32)/255
y_test=tf.constant(y_test,dtype=tf.int32)


# - 现在对数据进行切片并打包

# In[5]:


train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128)
test_db=tf.data.Dataset.from_tensor_slices((x_test,y_test)).batch(128)
loss_data=[]


# - 对权重进行初始化

# In[6]:


w1=tf.Variable(tf.random.truncated_normal([784,256],stddev=0.1))
b1=tf.Variable(tf.zeros([256]))

w2=tf.Variable(tf.random.truncated_normal([256,128],stddev=0.1))
b2=tf.Variable(tf.zeros([128]))

w3=tf.Variable(tf.random.truncated_normal([128,10],stddev=0.1))
b3=tf.Variable(tf.zeros([10]))

lr=1e-3


# - 向前传播

# In[16]:


for epoch in range(50):
for step,(x,y) in enumerate(train_db):
x=tf.reshape(x,[-1,28*28])

with tf.GradientTape() as tape:
h1= x@w1 + b1
h1=tf.nn.relu(h1)

h2= h1@w2 + b2
h2=tf.nn.relu(h2)

out= h2@w3 +b3

y_onehot=tf.one_hot(y,depth=10)

loss=tf.square(y_onehot - out)
loss=tf.reduce_mean(loss)

grads=tape.gradient(loss,[w1,b1,w2,b2,w3,b3])

w1.assign_sub(lr*grads[0])
b1.assign_sub(lr*grads[1])
w2.assign_sub(lr*grads[2])
b2.assign_sub(lr*grads[3])
w3.assign_sub(lr*grads[4])
b3.assign_sub(lr*grads[5])

if step % 100 == 0:
print(epoch,step,'loss',float(loss))
loss_data.append(loss)


# In[17]:


plt.plot(loss_data)
plt.show()


# In[18]:


total_correct,total_num=0,0
for step,(x,y) in enumerate(test_db):
x=tf.reshape(x,[-1,28*28])

h1=tf.nn.relu(x@w1+b1)
h2=tf.nn.relu(h1@w2+b2)
out=h2@w3+b3
prob=tf.nn.softmax(out,axis=1)
preb=tf.argmax(prob,axis=1)
preb=tf.cast(preb,dtype=tf.int32)

correct=tf.cast(tf.equal(preb,y),dtype=tf.int32)
correct=tf.reduce_sum(correct)

total_correct+=int(correct)
total_num+=x.shape[0]

acc=total_correct/total_num
print('test_acc:',acc)