TensorFlow學習筆記—基礎操作

TensorFlow

在上篇博客:深度學習筆記—線性迴歸(實戰)中,我們使用了常規方法實現Boston房價的預測,效果很明顯——費力不討好,代碼實現複雜,基本上是公式的實現,耗費時間長,損失下降不明顯,如果我們真拿這個去交付給用戶使用,恐怕會被罵個狗血淋頭,所以我們得建立一個更好的,效率更高的,更穩定的模型。
人和動物的區別就是是否會使用工具,我們要想更高效的建立和訓練模型,也需要學會使用工具—TensorFlow
TensorFlow是一個基於數據流編程(dataflow programming)的符號數學系統,被廣泛應用於各類機器學習(machine learning)算法的編程實現,其前身是谷歌的神經網絡算法庫DistBelief 。
Tensorflow擁有多層級結構,可部署於各類服務器、PC終端和網頁並支持GPU和TPU高性能數值計算,被廣泛應用於谷歌內部的產品開發和各領域的科學研究 。
TensorFlow由谷歌人工智能團隊谷歌大腦(Google Brain)開發和維護,擁有包括TensorFlow Hub、TensorFlow Lite、TensorFlow Research Cloud在內的多個項目以及各類應用程序接口(Application Programming Interface, API) 。自2015年11月9日起,TensorFlow依據阿帕奇授權協議(Apache 2.0 open source license)開放源代碼 (摘自百度百科) 。
簡單來說,TensorFlow就是一個核心開源庫,可以幫助我們開發和訓練機器學習模型,如果想了解具體信息可以訪問TensorFlow官網進行了解,在這裏我們就不贅述了。

TensorFlow基礎

  1. TensorFlow中的數據類型

在上一節的代碼實現中,我們用了python自帶的數據類型和numpy的數據類型,但是這些數據類型都是早於TensorFlow出現的,導致這些數據類型在TensorFlow不能很好的使用,比如numpy中的array數據類型就不能自動求導,也不能支持GPU運算,所以TensorFlow自己定了了一種數據類型tensor,簡單來講,tensor就是TensorFlow可以接受的數據,幾乎所有的數據都可以叫做tensor,包括而不僅限於:

  • 標量(scalar)

標量就是一個單獨的數。

  • 向量(vector)

一個向量是一列數

  • 矩陣(matrix)

矩陣是一個二維數組

  • 張量(tensor)

一般的,一個數組中的元素分佈在若干維座標的規則網格中,我們稱之爲張量。

tensor支持的數據類型

  • int float double
  • bool
  • string

action:tensor支持的所有數據類型都要用tf.int或tf.string等這種表示方法。
我們從名字發現一個很有趣的東西,TensorFlow支持的數據類型爲tensor,名字爲TensorFlow的前一部分單詞,剩下的單詞爲Flow,意爲滑動,流動的意思。這生動的解釋了TensorFlow的原理,數據在管道中流動處理,再從出口出來,給出我們想要的結果。

  1. 創建tensor

TensorFlow提供了大量功能豐富且繁雜的API接口供使用者調用,除了很複雜的操作外,初學者總能很輕易的找到合適的API實現你想要的功能

但是在使用TensorFlow之前,首先要導入TensorFlow庫:

import tensorflow as tf

首先我們來創建一個tensor:

From Numpy List

因爲TensorFlow的出現要晚於numpy的出現,所以TensorFlow設計的很多功能和API都學習了numpy的設計,並且對numpy的數據結構也專門設計了方法進行轉換:

convert_to_tensor()

convert_to_tensor(
    value,
    dtype=None,
    name=None,
    preferred_dtype=None
)

from Numpy

tf.convert_to_tensor(np.ones([2,3]))
tf.convert_to_tensor(np.zeros([2,3]))

該API可numpy中的矩陣直接轉換爲tensor,並且不僅限於numpy中矩陣,對於list也可直接轉化:

tf.convert_to_tensor([1,2])
tf.convert_to_tensor([1,2.])
tf.convert_to_tensor([1],[2.])

我們可以觀察第一行代碼和第二行代碼,唯一的區別就是第一行第二列的數據類型不同,該API可自動配合合適的數據類型。而第二行和第三行代碼的區別則是數據維度不同,在這裏我們不再贅述。

constant()

tf.constant(
    value,
    dtype=None,
    shape=None,
    name='Const',
    verify_shape=False
)

創建一個張量,傳入list或者數值來填充

使用示例:

tf.constant(1)
tf.constant([1])
tf.constant([1,2.])
tf.constant([1,2.],[3.])#錯誤用法

注意,若傳入多維數據,則每個維度上的數據數量必須一致,否則將發生錯誤。

zeros() and ones()

tf.zeros(
    shape,
    dtype=tf.float32,
    name=None
)

tf.ones(
	shape,
	dtype=tf.float32, 
	name=None
)

用法示例

tf.zeros([])
tf.zeros([1])
tf.zeros([2,2])
tf.zeros([2,3,3])

tf.ones(1)
tf.ones([2])
tf.ones([2,3])

zeros()和ones() 就像一對孿生兄弟,輸入想要的維度,API自動幫你填充0或1,一目瞭然。

zeros_like() and ones_like()

tf.zeros_like(
		tensor, 
		dtype=None, 
		name=None
)

tf.ones_like(
		tensor,
		dtype=None, 
		name=None
 )

用法示例

a = tf.zeros([2,3,3])
tf.zeros_like(a)
tf.ones_like(a)

除去去ones孿生兄弟外,tensorflow還提供了ones_lie()這對孿生兄弟,但是不同的是,ones兄弟需要給定tensor維度,而ones_like()兄弟則需要給定一個tensor作爲參數,該API按照tensor的維度重新建立一個tensor。

fill()

tf.fill(
		dims, 
		value, 
		name=None
	)

dims: 類型爲int32的tensor對象,用於表示輸出的維度(1-D, n-D),通常爲一個int32數組,如:[1], [2,3]等

value: 常量值(字符串,數字等),該參數用於設置到最終返回tensor對象中的值

name: (可選)當前操作別名

用法示例:

tf.fill([2,2],0)
tf.fill([2,2],0.0)
tf.fill([2,2],1)

fill()是一種很靈活的建立tensor的方法,只需要修改填入的數值就可以很輕鬆的取代ones兄弟和ones_like()兄弟。

random.normal()=

用於從服從指定正太分佈的數值中取出隨機數。一般用於參數初始值的賦值。

tf.random_normal(
    shape,
    mean=0.0,
    stddev=1.0,
    dtype=tf.float32,
    seed=None,
    name=None
)

shape: 輸出張量的形狀,必選

mean: 正態分佈的均值,默認爲0

stddev: 正態分佈的標準差,默認爲1.0

dtype: 輸出的類型,默認爲tf.float32

seed: 隨機數種子,是一個整數,當設置之後,每次生成的隨機數都一樣

name: 操作的名稱

使用示例:

tf.random.normal([2,2],mean=1,stddv=1)#正態分佈 均值 方差
tf.random.normal([2,2])#默認均值方差均爲0

random.truncated_normal()

輸出截斷的正態分佈的隨機值。說的簡單些,就是輸出正態分佈的一部分。

tf.random.truncated_normal(
    shape,
    mean=0.0,
    stddev=1.0,
    dtype=tf.dtypes.float32,
    seed=None,
    name=None
)

shape:一個一維整數張量或Python數組。輸出張量的形狀。

mean:一個零維張量或Python值,類型爲dtype。截斷正態分佈的均值。均值默認爲0

stddev:一個零維張量或Python值,類型爲dtype。正態分佈的標準偏差,在截斷。截斷前正態分佈的標準偏差,默認爲1.0

dtype:輸出的類型。

seed:一個Python整數。用於爲分佈創建隨機種子。

名稱:操作的名稱(可選)。

使用示例:

tf.random.truncated_normal([2,2],mean=0,stddev=1)#截斷方差

random.uniform()

random_uniform(
    shape,# 生成的張量的形狀
    minval=0,
    maxval=None,
    dtype=tf.float32,
    seed=None,
    name=None
)

shape:一維整數張量或 Python 數組.輸出張量的形狀
.
minval:dtype 類型的 0-D 張量或 Python 值;生成的隨機值範圍的下限;默認爲0.

maxval:dtype 類型的 0-D 張量或 Python 值.要生成的隨機值範圍的上限.如果 dtype 是浮點,則默認爲1 .

dtype:輸出的類型:float16、float32、float64、int32、orint64.

seed:一個 Python 整數.用於爲分佈創建一個隨機種子.查看
tf.set_random_seed 行爲.

name:操作的名稱(可選).

使用示例:

tf.random.uniform([2,2],minval=0,maxval=1)#均勻分佈
tf.random.uniform([2,2],minval=0,maxval=10)

跟random.normal()類似,random.uniform()同樣是給出隨機值,便於參數更新。

  1. 索引與切片

我們在學習java,c,以及python等編程語言時,可以很方便的使用指針或者或者索引解決找位置的爲題,在tensorflow中,爲了方便尋找數據,也提供了功能強大是索引方法,與java數組中的索引方式類似。

方法一

import tensorflow as tf
import numpy as np
a = tf.ones([1,5,5,3])
#(1, 5, 5, 3)
print(a.shape)
(5, 3)
print(a[0][0].shape)
#(3,)
print(a[0][0][0].shape)
#()
print(a[0][0][0][2].shape)

tensorflow中的索引方式與numpy矩陣中的索引方式類似,用法也極爲相像。

方法二

import tensorflow as tf
a = tf.range(10)
print(a)
print(a[3:5].shape)
#(1.)
print(a[-1:].shape)

注意:此方法僅適用於向量。

方法三

import tensorflow as tf
import numpy as np
a = tf.ones([5,5,5,5])
#(5,5,5)
print(a[2,:,:,:].shape)
#(5,5)
print(a[2,2,:,:].shape)
#(5,5,5)
print(a[:,:,:,3].shape)

:用來佔位,即表示該維度全選。

方法四

import tensorflow as tf
import numpy as np
a = tf.ones([5,5,5,5])
#(1,5,5,5)
print(a[2:3,:,:,:].shape)
#(3,5,5,5)
print(a[2:,:,:,:].shape)
#(2,1,5,5)
print(a[2:,2,:,:].shape)

實際上這種切片方法很像是方法二和方法三的混合體,實際上效果也是驗證了。

以上四種方法雖然能很便捷的截取tensor的一部分,但是都存在侷限性,只能選取某一維度上的某一位置的所有數據,或者是某 一維度上的某幾個連續位置上的所有數據,這明顯不方便我們對數據使用和處理,所以tensor也提供了幾個特殊的方法,滿足我們的需求。

gather

gather(
    params,
    indices,
    validate_indices=None,
    name=None
)

params:是一個tensor,

indices:是個值爲int的tensor用來指定要從params取得元素的第0維的index。

validate_indices:針對哪一維度進行的操作。

name:操作的名字。

使用示例:

import tensorflow as tf
import numpy as np
a = tf.random.normal([2,4,28,28,3])
#(2, 2, 28, 28, 3)
print(tf.gather(a,axis=1,indices=[2,3]).shape)


gather一詞是收集的意思,從結果上我們可以很清楚的看到,確實是收集,針對某一維度的某幾個不連續的位置進行數據提取操作,並且組成一個新的張量。

gather_nd

gather_nd(
    params,
    indices,
    name=None
)

params:張量.這個張量是用來收集數值的.

indices:張量.必須是以下類型之一:int32,int64;索引張量.

name:操作的名稱(可選).

使用示例:

import tensorflow as tf
import numpy as np
a = tf.random.normal([2,4,28,28,3])
#(4, 28, 28, 3)
print(tf.gather_nd(a,[0]).shape)
#(28, 28, 3)
print(tf.gather_nd(a,[0,1]).shape)
#(28, 3)
print(tf.gather_nd(a,[0,1,2]).shape)
#(1, 28, 3)
print(tf.gather_nd(a,[[0,1,2]]).shape)
#(3, 4, 28, 28, 3)
print(tf.gather_nd(a,[[0],[1],[2]]).shape)
#(2, 28, 28, 3)
print(tf.gather_nd(a,[[0,1],[1,3]]).shape)

從示例中我們可以看到,gather_nd()是一個比gather更爲靈活的方法,不僅可以java中索引式取某一維度上所有的數據,還能像gather()方法一樣,只取部分數據,並且合併成一個新的張量。

boolean_mask()

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

tensor:N維度的tensor,

mask:由布爾值構成的K維度的列表,注意K小於等於N,

name:可選項也就是這個操作的名字,

axis是一個0維度的int型數字,表示的是從參數tensor的哪個axis開始過濾。

使用示例:

a=tf.random.normal([4,28,28,3])
#([2, 28, 28, 3])
print(tf.boolean_mask(a,mask=[True,True,False,False]).shape)
#([4, 28, 28, 2])
print(tf.boolean_mask(a,mask=[True,True,False],axis=3).shape)

boolean_mask 是依據索引出的TRUE和False值判斷是否去該維度的數據,如果僅僅只是那麼簡單,爲什麼還要設計這個功能呢,豈不是重複了,所以boolean_mask的功能沒有那麼簡單。

import tensorflow as tf
import numpy as np
a = tf.random.normal([2,3,4])
#(4,4)
print(tf.boolean_mask(a,mask=[[True,False,True],[False,True,True]]).shape)
#(3,4)
print(tf.boolean_mask(a,mask=[[True,False,False],[False,True,True]]).shape)

在boolean_mask()函數中,將mask參數換成多維列表,則可以與tensor中的維度相對應取值,更爲方便快捷。

  1. 維度變換
    reshape
tf.reshape(
    tensor,
    shape,
    name=None
)

tensor:需要進行維度變換的的張量。

shape:張量要變成的形狀。

name:操作名稱。

使用示例:

import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(4, 784, 3)
print(tf.reshape(a,[4,28*28,3]).shape)
#(4, 784, 3)
print(tf.reshape(a,[4,-1,3]).shape)
#
print(tf.reshape(a,[4,-1]).shape)
#(4, 1, 784, 3)
print(tf.reshape(tf.reshape(a,[4,-1]),[4,-1,784,3]))

transpose()

tf.transpose(
    matrix,
    perm=None
    )

matrix:待變換的張量。

perm:張量變換方式的具體參數。

使用示例:

import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(3, 28, 28, 4)
print(tf.transpose(a).shape)
#(4, 28, 3, 28)
print(tf.transpose(a,perm=[0,1,3,2]).shape)

從使用示例上來看,transpose方法和reshape方法在功能上有些重合,但是在transpose方法更爲靈活,在不指定matrix參數時,transpose方法默認將tensor的維度倒序,同時在指定matrix參數指定爲列表時,會按照列表排序來變換。

expand_dims()

tf.expand_dims(
    input,
    axis=None,
    name=None,
    dim=None
)

input是輸入張量。

axis是指定擴大輸入張量形狀的維度索引值。

dim等同於軸,一般不推薦使用。

使用示例;

import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(1, 4, 28, 28, 3)
print(tf.expand_dims(a,axis=0).shape)
#(4, 28, 28, 3, 1)
print(tf.expand_dims(a,axis=-1).shape)

從示例中我們可以很清楚的看出來,expand_dims()方法用於向張量中加入一個新的維度,而axis參數可控制添加維度的位置。

squeeze()

squeeze(
    input,
    axis=None,
    name=None,
    squeeze_dims=None
)

input: 需要壓縮的tensor

axis: 如果指定,只能擠壓列出的維度。維度索引從 0 開始。壓縮非 1 的維度是錯誤的,並且必須在範圍內。
name: 操作的名稱 (可選)

squeeze_dims: 已棄用的關鍵字參數,現在是axis。

使用示例:

import tensorflow as tf
import numpy as np
a = tf.random.normal([4,28,28,3])
#(2, 3)
print(tf.squeeze(tf.zeros([1,2,1,1,3])).shape)
b =  tf.ones([1,2,1,3])
#(2, 1, 3)
print(tf.squeeze(b,axis=0).shape)
#(1, 2, 3)
print(tf.squeeze(b,axis=2).shape)

該函數返回一個張量,這個張量是將原始input中所有維度爲1的那些維都刪掉的結果
axis可以用來指定要刪掉的爲1的維度,此處要注意指定的維度必須確保其是1,否則會報錯。如果axis不指定,則默認刪除所有爲1的維度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章