学习视频来源为b站动手学深度学习系列视频:https://space.bilibili.com/209599371/channel/detail?cid=23541
由于上述视频为MXNet/Gluon框架编写,所以代码部分参考网站为:https://trickygo.github.io/Dive-into-DL-TensorFlow2.0/#/
本文主要是学习该系列视频所整理笔记,可能很多内容直接原文整理,如有需要可以去上述两个网站进行学习。
这个问题不同人有不同的理解,我粗浅的理解就是,模拟人脑的结构对数据进行观察,并发现数据潜在的特征。
这样说可能不太形象,举个例子:一个桶里有铁屑和木屑,机器学习是通过一种方法来进行分离,例如SVM思想,往桶里倒入水,就可以将木屑和铁屑从二维平面映射到三维空间,从而实现分离;而深度学习例如人去观察铁屑和木屑的不同,然后提取出特征,然后快速识别出来(人一个一个把铁屑挑出来的感觉,当然计算机模拟的人脑挑的速度足够快)。
以上是个人理解,欢迎大家纠正等。
这里其实课程中主要讲述了一些实践,详细原理没怎么说。
引入tensorflow库,并命名为tf。(可以不命名,但是命名比较方便,试试就能体会出来)
import tensorflow as tf
输出tensorflow版本
print(tf.__version__)
然后定义一个tf矩阵/张量
x = tf.constant([1,2,3,4,5,6,7,8,9,10,11,12])
这里解释下张量的概念,类型名Tensor,如果大家使用过numpy或者pandas可以发现,创建或者读取的矩阵是这两个裤特有的类型,同样tensorflow的变量也有一个特殊的称呼,叫做张量,可以理解成tensorflow的特有结构,其本质就是个矩阵...不太理解就当成矩阵就好。(下属可能说张量/矩阵时直接以矩阵代替)
张量/矩阵的改变形状/维度
X = tf.reshape(x,(3,4))
#输出结果Tensor("Reshape:0", shape=(3, 4), dtype=int32)
#上面的x.reshape((3, 4))也可写成x.reshape((-1, 4))或x.reshape((3, -1))。
# 由于x的元素个数是已知的,这里的-1是能够通过元素个数和其他维度的大小推断出来的。
(x原本为[1,2,3,4,5,6,7,8,9,10,11,12],现在改变成3*4的矩阵了)
创建0、1矩阵
#创建0向量,纬度为2*3*4
x=tf.zeros((2,3,4))
print(x)
#创建1向量,纬度为2*3*4
x=tf.ones((2,3,4))
print(x)
创建自定义矩阵
x = tf.constant([1,2,3,4,5,6,7,8,9,10,11,12])
输出矩阵形状/维度
print(x.shape)
张量类型转换
#cast数据类型转换
Y = tf.cast(Y, tf.float32)
按元素进行矩阵加减乘除以及指数运算等
#元素加法
print("加法",X+Y)
#元素乘法除法
print("乘法",X*Y)
print("除法",X/Y)
#按元素元素指数运算
Y = tf.cast(Y, tf.float32)
print("指数",tf.exp(Y))
(元素类型不同是没法操作的,使用前先用cast函数统一类型哟~~)
广播机制:当对两个形状不同的tensor
按元素运算时,可能会触发广播(broadcasting)机制:先适当复制元素使这两个tensor
形状相同后再按元素运算。(copy自上述引文)
假设A和B分别是3行1列和1行2列的矩阵,如果要计算A + B,那么A中第一列的3个元素被广播(复制)到了第二列,而B中第一行的2个元素被广播(复制)到了第二行和第三行。如此,就可以对2个3行2列的矩阵按元素相加。
矩阵乘法、连结等操作
#矩阵乘法
print("矩阵除法",tf.matmul(X, tf.transpose(Y)))
#矩阵连结,二维的包括行连结和列连结,由axis决定,高维度的可以其他维度连结
print("行连结",tf.concat([X,Y],axis = 0))
print("列连结",tf.concat([X,Y],axis = 1))
判断两矩阵是否相等
#判断两个元素是否相等
print(tf.equal(X,Y))
张量元素求和
sess = tf.Session()
print("张量",tf.reduce_sum(X))
print("张量值",sess.run(tf.reduce_sum(X)))
注意!输出张量和输出张量值是不一样的,前者输出的是这个张量的各种信息,例如张量名称、形状、类型等,后者即为张量的值,也就是矩阵的值,上述一直输出的都是张量,而不是张量值。本模块进行输出就可以发现区别了(本文编译器为pycharm,好像jupyter可以直接输出张量值...)
按索引输出
print("索引",sess.run(X[0:2]))
print("索引",sess.run(X[0:2][1]))
print("索引",sess.run(X[0:2][1][1]))
(自己动手输出一下就知道多加[]是什么意思了~很有趣)
修改矩阵数值
X = tf.Variable(X)
init = tf.global_variables_initializer()
sess.run(init)
#修改X变量,但是需要进行初始化才能进行操作
#修改某1,2位置数为19
print(sess.run(X[1,2].assign(19)))
#修改第一列为99
print(sess.run(X[1:2,:].assign(tf.ones(X[1:2,:].shape, dtype = tf.int32)*99)))
(此处需要了解一些tensorflow的初始化机制,本文就不具体介绍了,其实我目前也不是很了解哈~都是报错后才想起来(小声音嘟嘟,我比较菜~))
在前面的例子里我们对每个操作新开内存来存储运算结果。举个例子,即使像Y = X + Y
这样的运算,我们也会新开内存,然后将Y
指向新内存。为了演示这一点,我们可以使用Python自带的id
函数:如果两个实例的ID一致,那么它们所对应的内存地址相同;反之则不同。(copy自引文)
验证程序:
#内存开销
X = tf.Variable(X)
Y = tf.cast(Y, dtype=tf.int32)
before = id(Y)
Y = Y + X
print(id(Y) == before)
#输出为否
两种避免的写法:
如果想指定结果到特定内存,我们可以使用前面介绍的索引来进行替换操作。在下面的例子中,我们先通过zeros_like
创建和Y
形状相同且元素为0的tensor
,记为Z
。接下来,我们把X + Y
的结果通过[:]
写进Z
对应的内存中。
Z = tf.Variable(tf.zeros_like(Y))
before = id(Z)
Z[:].assign(X + Y)
print(id(Z) == before)
#输出为真
实际上,上例中我们还是为X + Y
开了临时内存来存储计算结果,再复制到Z
对应的内存。如果想避免这个临时内存开销,我们可以使用assign_{运算符全名}
函数。
before = id(X)
X.assign_add(Y)
print(id(X) == before)
#输出为真
tensorflow和numpy的转换
其实通过发现num的数据类型和tensorflow的数据类型虽然不一样,其本质都是矩阵(包括pandas),所以有特定的函数进行类型转化
np转tf
P = np.ones((2,3))
D = tf.constant(P)
tf转np
np.array(D)
自动求梯度
神经网络中最不可缺少的就是自动求梯度这一方法~(懂的应该深有体会...)
设y=2*x*x,其导数应为4x。
求解过程如下:
import tensorflow as tf
sess = tf.Session()
#新建一个矩阵
x = tf.reshape(tf.Variable([1,2,3,4], dtype=tf.float32),(4,1))
print(x)
with tf.GradientTape() as t:
t.watch(x)
y = 2 * tf.matmul(tf.transpose(x), x)
dy_dx = t.gradient(y, x)
#如果不初始化是无法run的
init = tf.global_variables_initializer()
sess.run(init)
print(sess.run(dy_dx))
再写一个z、y公式详见代码内
with tf.GradientTape(persistent=True) as g:
g.watch(x)
y = x * x
z = y * y
dz_dx = g.gradient(z, x) # 108.0 (4*x^3 at x = 3)
dy_dx = g.gradient(y, x) # 6.0
print(sess.run(dz_dx),sess.run(dy_dx))
教程中还提供一个方法,我没怎么看懂。。。挂个代码,大家自己研究研究吧
#控制流求梯度
def f(a):
b = a * 2
while tf.norm(b) < 1000:
b = b * 2
if tf.reduce_sum(b) > 0:
c = b
else:
c = 100 * b
return c
经常我们使用各种框架或者函数,不了解参数、返回值或者含义等就可以进行查询其文档来了解具体含义
当我们想知道一个模块里面提供了哪些可以调用的函数和类的时候,可以使用dir
函数。下面我们打印dtypes
和random
模块中所有的成员或属性。(copy自引文)
print(dir(tf.dtypes))
print(dir(tf.random))
想了解某个函数或者类的具体用法时,可以使用help
函数。让我们以ones
函数为例,查阅它的用法。(copy自引文)
print(help(tf.ones))