TensorFlow 學習指南 二、線性模型
譯者:飛龍
自豪地採用谷歌翻譯
廣播
當我們操作不同維度的陣列時,它們可以以不同的方式組合,無論是逐元素還是通過廣播。
讓我們從頭開始,構建更復雜的例子。 在下面的示例中,我們有表示單個數字的 TensorFlow 常量。
import tensorflow as tf
a = tf.constant(3, name='a')
with tf.Session() as session:
print(session.run(a))
這裡沒什麼驚喜! 我們也可以進行計算,例如將其加上另一個數字:
a = tf.constant(3, name='a')
b = tf.constant(4, name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
讓我們將這個概念擴充套件到一個數字列表。 首先,讓我們建立一個包含三個數字的列表,然後建立另一個數字列表:
a = tf.constant([1, 2, 3], name='a')
b = tf.constant([4, 5, 6], name='b')
add_op = a + b
with tf.Session( ) as session:
print(session.run(add_op))
這稱為逐元素操作,其中依次考慮每個列表中的元素,將它們相加,然後合併結果。
如果我們將這個列表和僅僅一個數字相加,會發生什麼?
a = tf.constant([1, 2, 3], name='a')
b = tf.constant(4, name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
這是你所期望的嗎? 這被稱為廣播操作。 我們的主要物件引用是a
,它是一個數字列表,也稱為陣列或一維向量。 與單個數字(稱為標量)相加會產生廣播操作,其中標量將與列表的每個元素相加。
現在讓我們看一個擴充套件,它是一個二維陣列,也稱為矩陣。 這個額外的維度可以被認為是“列表的列表”。 換句話說,列表是標量的組合,矩陣是列表的列表。
也就是說,矩陣上的操作如何工作?
a = tf.constant([[1, 2, 3], [4, 5, 6]], name='a')
b = tf.constant([[1, 2, 3], [4, 5, 6]], name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
這是逐元素的。 如果我們加上一個標量,結果是可以預測的:
a = tf.constant([[1, 2, 3], [4, 5, 6]], name='a')
b = tf.constant(100, name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
事情開始變得棘手。 如果我們將一維陣列與二維矩陣相加會發生什麼?
a = tf.constant([[1, 2, 3], [4, 5, 6]], name='a')
b = tf.constant([100, 101, 102], name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
在這種情況下,陣列被廣播為矩陣的形狀,導致陣列與矩陣的每一行相加。 使用此術語,矩陣是行的列表。
如果我們不想要這個,而是想將矩陣的列與b
相加呢?
a = tf.constant([[1, 2, 3], [4, 5, 6]], name='a')
b = tf.constant([100, 101,], name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
這不起作用,因為 TensorFlow 試圖按照行廣播。 它不能這樣做,因為b
中的值的數量(2)與每行中的標量數量(3)不同。
我們可以通過從列表中建立一個新矩陣來執行此操作。
a = tf.constant([[1, 2, 3], [4, 5, 6]], name='a')
b = tf.constant([[100], [101]], name='b')
add_op = a + b
with tf.Session() as session:
print(session.run(add_op))
這裡發生了什麼? 要理解這一點,讓我們看一下矩陣形狀。
a.shape
TensorShape([Dimension(2), Dimension(3)])
b.shape
TensorShape([Dimension(2), Dimension(1)])
你可以從這兩個示例中看到a
有兩個維度,第一個大小為 2,第二個大小為 3。換句話說,它有兩行,每行有三個標量。
我們的常數b
也有兩個維度,兩行,每行一個標量。如果有一行兩個標量,這與列表不同,也與矩陣不同。
由於形狀在第一維匹配,而第二維不匹配的事實,廣播發生在列而不是行中。 廣播規則的更多資訊請參見此處。
建立一個三維矩陣。 如果將其與標量,陣列或矩陣相加,會發生什麼?
使用tf.shape
(這是一個操作)在圖的操作期間獲得常量的形狀。
考慮更高維矩陣的用例。 換句話說,在哪裡你可能需要 4D 矩陣,甚至是 5D 矩陣? 提示:考慮集合而不是單個物件。
隨機性
機器學習模型是許多變數的複雜集合,但必須經過訓練才能找到好的值。這也意味著必須將這些“權重”設定為初始值。一種選擇是從所有權重為零開始。但是,這會在演算法上引起問題 - 基本上,錯誤的梯度無法修復錯誤。相反,我們經常將這些權重設定為隨機值。然後,模型學習並調整。
TensorFlow 有許多用於生成隨機數的內建方法。這包括我們熟悉的分佈,如“均勻”,以及你可能聽說過的其他分佈,如“正態”分佈。均勻分佈就像你擲骰子時得到的東西那樣 - 有一組值,它們都是等可能的。正態分佈是統計課程中教授的標準,其中資料具有更可能的平均值,以及圍繞它的“鐘形”曲線。我們將看到的,其他的也包括在內。
在本節中,我們將建立一個基本的輔助函式,它只執行一個 TensorFlow 變數。這個小函式非常有用!它建立一個會話,初始化變數併為我們執行它。它僅限於單個變數,因此對於較大的程式可能沒有用。
import tensorflow as tf
def run_variable(variable):
tf.initialize_all_variables()
with tf.Session() as sess:
return sess.run(variable)
希望現在這對你來說都很熟悉。 如果沒有,請再看看第一章,開始吧。
讓我們從一個基本的分佈開始,均勻分佈。
my_distribution = tf.random_uniform((6, 4), seed=42)
uniform = run_variable(my_distribution)
這為我們提供了一個 6 乘 4 的張量(隨機值的更多資訊,請參閱上一節)。為了視覺化,我們可以使用直方圖:
from matplotlib import pyplot as plt
plt.hist(uniform.flatten())
plt.show()
請注意,如果你使用的是 Jupyter 筆記本,請使用%matplotlib inline
並刪除plt.show()
行。
所得影象顯示了圖片,雖然還不是很清楚…
此直方圖顯示可能的值介於 0 和 1 之間。每個值應該是等可能的,但它看起來並不是那樣。 原因是我們只選擇了少量的值。 如果我們增加陣列的大小,它會變得更加均勻。
large_normal = tf.random_uniform((600, 400), seed=42)
large_uniform = run_variable(large_normal)
plt.hist(large_uniform.flatten())
plt.show()
更均勻了!
如果你沒有任何其他資訊,對於在機器學習模型中初始化權重,均勻分佈非常有用。 它也是一個“有界”分佈,它具有設定的最小值和最大值,隨機值不能超出該範圍。 要更改範圍,例如更改為 0 和 10,請乘以範圍並新增最小值。 在課程結束時有一個練習。
另一種常用的分佈是正態分佈,在 TensorFlow 中實現為random_normal
函式:
distribution = tf.random_normal((600, 4), seed=42)
normal = run_variable(distribution)
plt.hist(normal.flatten())
plt.show()
預設情況下,此分佈的平均值約為 0,標準差為 1。這些值不受限制,但越來越不可能偏離平均值,標準差設定了可能性減小的速率。 在實踐中,大約 60% 的值落在距離平均值一個標準差的“半徑”內,並且 99% 落在 4 個標準差內。
均值和標準差是random_normal
函式的引數。 例如,身高可近似建模為正態分佈,平均值約為 170cm,標準差約為 15cm。
distribution = tf.random_normal((10000,), seed=42, mean=170, stddev=15)
normal = run_variable(distribution)
plt.hist(normal.flatten())
plt.show()
到目前為止,我們的直方圖使用matplotlib
生成。 我們也可以使用 TensorFlow 來建立它們!histogram_fixed_width
函式接受值的列表(如我們的隨機值),範圍和要計算的桶數。 然後計算每個桶的範圍內有多少個值,並將結果作為陣列返回。
import numpy as np
bins = tf.histogram_fixed_width(normal, (normal.min(), normal.max()), nbins=20)
histogram_bins = run_variable(bins)
x_values = np.linspace(normal.min(), normal.max(), len(histogram_bins))
plt.bar(x_values, histogram_bins,)
在plt.bar
呼叫中,我們再次手動生成bin
值,然後使用條形圖將這些值繪製為x
值,並使用histogram_bins
作為高度。
這是正確的,但看起來不對。 直方圖的值在那裡,但寬度非常窄(我們的箱桶僅由單個值表示)。 我們來解決這個問題:
bar_width = (normal.max() - normal.min()) / len(histogram_bins)
plt.bar(x_values, histogram_bins, width=bar_width)
- 使用均勻分佈建模單次擲骰子。 繪製結果來確保其符合你的期望
- 使用單個圖中的純 TensorFlow 呼叫替換本課程的最後一個程式碼塊。 換句話說,使用 TensorFlow 概念來替換
.min()
,.max()
和len
呼叫。 只有繪圖在沒有 TensorFlow 的情況下進行!
線性方程
通過tf.solve
函式,TensorFlow 可以求解線性方程組。 你可能會將這些視為連線的方程,如下所示:
這些型別的線性方程用於數學中的許多問題,從優化工廠輸出到幾何。 你可以使用多種方法解決這些方程,但在本課中,我們將瞭解如何使用tf.solve
為我們執行此操作。
我將專注於幾何。 這是位於二維(x, y)
空間的兩個點,p1
和p2
:
這是他們在圖上的樣子:
要在 TensorFlow 中執行此操作,我們首先設定線性方程組,我們的點位於中心。 首先,我們建立我們的點矩陣。 第一行對應於第一個點,第二行對應於第二個點。 同樣,第一列是x
值,而第二列是y
值。
import tensorflow as tf
# 點 1
x1 = tf.constant(2, dtype=tf.float32)
y1 = tf.constant(9, dtype=tf.float32)
point1 = tf.stack([x1, y1])
# 點 2
x2 = tf.constant(-1, dtype=tf.float32)
y2 = tf.constant(3, dtype=tf.float32)
point2 = tf.stack([x2, y2])
# 將點組合為陣列
X = tf.transpose(tf.stack([point1, point2]))
直線的方程是:
重新排列方程(5),使x
和y
在同一側,我們得到以下結果:
我們的任務是在給定觀測點的情況下,找到上面的方程中的a
和b
的值。 我們可以通過取點陣列的逆並將其乘以一個矩陣,來輕易做到這一點。
使用矩陣(因為我們使用的是 TensorFlow),如果X
是我們觀察點的矩陣,而A
是我們需要學習的引數,我們設定一個系統:
接下來要學習的引數就是:
矩陣B
很簡單,適當廣播的數字 1,它源於上面方程的右側。
矩陣A
是上面方程 3 中的引數。
B = tf.ones((1, 2), dtype=tf.float32)
parameters = tf.matmul(B, tf.matrix_inverse(X))
with tf.Session() as session:
A = session.run(parameters)
最後一步是從上面的方程(5)中找到我們的a
和b
值,即從這些引數轉換(符合方程(7))。
b = 1 / A[0][1]
a = -b * A[0][0]
print("Equation: y = {a}x + {b}".format(a=a, b=b))
這個解決方案很好地包含在tf.solve
函式中。 為了看到它,讓我們看另一個例子。 這是一個圓圈:
以下是圓圈上的三個觀察點:
圓的規範方程是:
為了求解引數d
,e
和f
,我們建立另一個點陣列,並用 1 填充它來建立一個方陣。 我們正在尋找三個引數,因此我們的A
矩陣必須具有形狀(3, 3)
。
由於這個方程的平方部分沒有引數,當我們有x
和y
的觀測值時,我們的方程變得有點不同:
因此,我們的A
矩陣由x
和y
值(以及另一列 1)組成,我們的B
矩陣是負的x
和y
的平方和。
import tensorflow as tf
points = tf.constant([[2, 1],
[0, 5],
[-1, 2]], dtype=tf.float64)
A = tf.constant([
[2, 1, 1],
[0, 5, 1],
[-1, 2, 1]
], dtype='float64')
B = -tf.constant([[5], [25], [5]])
然後我們使用tf.matrix_solve
來找到我們的X
陣列,這是我們方程的引數。 在會話中執行它,我們得到三個值,即D
,E
和F
。
X = tf.matrix_solve(A, B)
with tf.Session() as session:
result = session.run(X)
D, E, F = result.flatten()
print("Equation: x**2 + y**2 + {D}x + {E}y + {F} = 0".format(**locals()))
1)求解包含以下三點的圓:P(2,1)
, Q(0,5)
, R(-1,2)
2)下面給出橢圓的一般形式。 解決以下幾點(解決這個方程需要五點):
橢圓的一般形式:
觀測點:
3D 中的 TensorFlow
TensorFlow 不僅僅是一個深度學習庫 - 它是一個但數值操作庫,因此它可以執行許多其他庫可以執行的任務。 在本課中,我們將介紹如何使用 TensorFlow 對 3D 物件執行操作。
3D 物件可以被建模為三維空間中的一系列三角形,我們通常將其稱為(x, y, z)
。 這些名稱不是必需的,但通常使用。 從這些 3D 點中的三個建立三角形。 點本身可以表示為大小為(3,)
的向量。 這些陣列是一個大小為(n, 3),
的矩陣,其中n
是我們擁有的點數。 讓我們深入去看一個基本的立方體。 我們稍後將需要此功能,所以讓我們建立一個繪製基本形狀的函式:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
def plot_basic_object(points):
"""繪製一個基本物件,假設它是凸的而不是太複雜"""
tri = Delaunay(points).convex_hull
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
S = ax.plot_trisurf(points[:,0], points[:,1], points[:,2],
triangles=tri,
shade=True, cmap=cm.Blues,lw=0.5)
ax.set_xlim3d(-5, 5)
ax.set_ylim3d(-5, 5)
ax.set_zlim3d(-5, 5)
plt.show()
如果你正在使用 Jupyter 筆記本,我建議執行這一行程式碼,它為你提供了一個非常棒的互動式 3D 繪圖。 左鍵單擊並拖動來左右移動,右鍵單擊並拖動來放大或縮小。
%matplotlib notebook
現在讓我們建立一個形狀。 下面的函式將返回組成立方體的六個點。 如果你回到上一個函式,你將看到 Delaunay 線,它將這些點轉換成三角形,以便我們可以渲染它們。
import numpy as np
def create_cube(bottom_lower=(0, 0, 0), side_length=5):
"""從給定的左下角點(最小的 x,y,z 值)開始建立一個立方體"""
bottom_lower = np.array(bottom_lower)
points = np.vstack([
bottom_lower,
bottom_lower + [0, side_length, 0],
bottom_lower + [side_length, side_length, 0],
bottom_lower + [side_length, 0, 0],
bottom_lower + [0, 0, side_length],
bottom_lower + [0, side_length, side_length],
bottom_lower + [side_length, side_length, side_length],
bottom_lower + [side_length, 0, side_length],
bottom_lower,
])
return points
現在讓我們把這些碎片放在一起,看看它是什麼樣的:
cube_1 = create_cube(side_length=2)
plot_basic_object(cube_1)
我只是在這裡顯示一個影象,但是你可以看到立方體,它已被我們的程式碼變成三角形並且顏色不同(取決於z
值)。 這很好,但現在讓我們使用 TensorFlow 對此進行一些操作。
平移
平移是一個簡單的動作:向上/向下,向左/向右,向前/向後,或這些的某種組合。 它是通過簡單地向每個點新增一個向量來建立的。 如果向所有點新增相同的向量,則整個物件將一致地移動。 檢視我們關於廣播的章節,瞭解當我們將大小為(3,)
的平移向量新增到大小(n, 3)
的點矩陣時會發生什麼。
import tensorflow as tf
def translate(points, amount):
return tf.add(points, amount)
points = tf.constant(cube_1, dtype=tf.float32)
# 更新此處的值來移動多維資料集。
translation_amount = tf.constant([3, -3, 0], dtype=tf.float32)
translate_op = translate(points, translation_amount)
with tf.Session() as session:
translated_cube = session.run(translate_op)
plot_basic_object(translated_cube)
旋轉
通過建立點積或旋轉矩陣和原點來形成旋轉。 旋轉物件首先需要你確定要旋轉的軸。 要圍繞特定軸旋轉,請將該軸的值設定為 0,相關軸中的值為 1。 你需要三個矩陣:
沿x
軸旋轉
[[1, 0, 0],
[0, c