1. 程式人生 > >TensorFlow 學習指南 二、線性模型

TensorFlow 學習指南 二、線性模型

原文:LearningTensorFlow.com

譯者:飛龍

協議:CC BY-NC-SA 4.0

自豪地採用谷歌翻譯

廣播

當我們操作不同維度的陣列時,它們可以以不同的方式組合,無論是逐元素還是通過廣播。

讓我們從頭開始,構建更復雜的例子。 在下面的示例中,我們有表示單個數字的 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)空間的兩個點,p1p2

這是他們在圖上的樣子:

要在 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),使xy在同一側,我們得到以下結果:

我們的任務是在給定觀測點的情況下,找到上面的方程中的ab的值。 我們可以通過取點陣列的逆並將其乘以一個矩陣,來輕易做到這一點。

使用矩陣(因為我們使用的是 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)中找到我們的ab值,即從這些引數轉換(符合方程(7))。

b = 1 / A[0][1]
a = -b * A[0][0]
print("Equation: y = {a}x + {b}".format(a=a, b=b))

這個解決方案很好地包含在tf.solve函式中。 為了看到它,讓我們看另一個例子。 這是一個圓圈:

以下是圓圈上的三個觀察點:

圓的規範方程是:

為了求解引數def,我們建立另一個點陣列,並用 1 填充它來建立一個方陣。 我們正在尋找三個引數,因此我們的A矩陣必須具有形狀(3, 3)

由於這個方程的平方部分沒有引數,當我們有xy的觀測值時,我們的方程變得有點不同:

因此,我們的A矩陣由xy值(以及另一列 1)組成,我們的B矩陣是負的xy的平方和。

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陣列,這是我們方程的引數。 在會話中執行它,我們得到三個值,即DEF

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