CNTK:邏輯迴歸
介紹:
問題描述:癌症醫院提供了資料,並希望我們確定患者是否有致命的惡性腫瘤或良性腫瘤。這類問題被稱為分類問題。為了幫助對每個病人進行分類,我們給予了他們的年齡和腫瘤的大小。直觀地,可以想象,年輕的患者或/和小腫瘤的患者不太可能患有惡性腫瘤。在下面的圖中,紅色表示惡性和藍色表示良性。注意:這是一個學習的例子; 在現實生活中,需要來自不同測試/檢查來源的許多特徵和醫生的專業知識將為患者做出診斷、治療決定。
from IPython.display import Image Image(url="https://www.cntk.ai/jup/cancer_data_plot.jpg", width=400, height=400)
目標:我們的目標是學習一個分類器,可以根據兩個特徵(年齡和腫瘤大小)自動將任何患者標記為良性或惡性。在本教程中,我們將建立一個線性分類器。
以下是分類的結果
Image(url ="https://www.cntk.ai/jup/cancer_classify_plot.jpg" , width = 400, height = 400)
在上圖中,綠線表示從資料中學習的模型,並將藍點與紅點分開。
任何學習演算法通常有五個階段。這些是資料讀取,資料預處理,建立模型,學習模型引數和評估模型(也稱為測試/預測)。
1.資料讀取:我們生成模擬資料集,每個樣本具有兩個特徵(如下所示),用來表示年齡和腫瘤大小。
2. 資料預處理:通常需要縮放各種feature(如大小或年齡)。
邏輯迴歸
邏輯迴歸在機器學習中是一種基本技術,它利用特徵的線性加權組合,併產生預測不同類別的概率。在文中,分類器的概率範圍為【0,1】,然後與設定的閾值(大多去取0.5)比較,進而產生二進位制標籤,0或1。這裡為二分類問題,所述方法也可以擴充套件到多分類問題。
由上圖可知,來自不同輸入特徵的貢獻是線性加權的。所得到的和通過Sigmoid函式對映到【0,1】範圍,對於具有兩個以上分類的,可以使用softmax函式
檢查是否安裝了CNTK,以及其版本
from __future__ import print_function
import numpy as np
import sys
import os
import cntk as C
if 'TEST_DEVICE' in os.environ:
if os.environ['TEST_DEVICE'] == 'cpu':
C.device.try_set_default_device(C.device.cpu())
else:
C.device.try_set_default_device(C.device.gpu(0))
if not C.__version__ == "2.0":
raise Exception("this notebook was designed to work with 2.0. Current Version: " + C.__version__)
資料生成
用numpy庫生成一些模擬癌症的資料。這裡定義了兩個輸入的特徵和兩個標籤。在示例中,訓練資料中每組資料都有一個標籤,良性或惡性,所以這裡為二分類問題。
定義網路
input_dim = 2
num_output_classes = 2
特徵和標籤
在本教程中使用numpy庫生成資料。
from __future__ import print_function
import numpy as np
import sys
import os
import cntk as C
# Plot the data
import matplotlib.pyplot as plt
# Define the network
input_dim = 2
num_output_classes = 2
# Ensure that we always get the same results
np.random.seed(0)
# Helper function to generate a random data sample
def generate_random_data_sample(sample_size, feature_dim, num_classes):
# Create synthetic data using NumPy.
Y = np.random.randint(size=(sample_size, 1), low=0, high=num_classes)
# Make sure that the data is separable
X = (np.random.randn(sample_size, feature_dim)+3) * (Y+1)
# Specify the data type to match the input variable used later in the tutorial
# (default type is double)
X = X.astype(np.float32)
# convert class 0 into the vector "1 0 0",
# class 1 into the vector "0 1 0", ...
class_ind = [Y==class_number for class_number in range(num_classes)]
Y = np.asarray(np.hstack(class_ind), dtype=np.float32)
return X, Y
# Create the input variables denoting the features and the label data. Note: the input
# does not need additional info on the number of observations (Samples) since CNTK creates only
# the network topology first
mysamplesize = 32
features, labels = generate_random_data_sample(mysamplesize, input_dim, num_output_classes)
# let 0 represent malignant/red and 1 represent benign/blue
colors = ['r' if label == 0 else 'b' for label in labels[:,0]]
plt.scatter(features[:,0], features[:,1], c=colors)
plt.xlabel("Age (scaled)")
plt.ylabel("Tumor size (in cm)")
plt.show()
為了確保每次的執行結果一樣,在生成隨機數的時候使用seed可以保障每次生成的隨機數是一樣的。然後使用numpy生成隨機數,然後視覺化資料,使用matplotlib畫圖。
模型建立
其數學形式為:
z=∑i=1nwi×xi+b=w⋅x+b
W是向量N的權重,b為偏差。使用sigmoid或softmax函式可以將和對映到0到1.
定義輸入
feature = C.input_variable(input_dim, np.float32)
在輸入中,如果要輸入10*5pixel圖片,那麼該函式要寫作為C.input_variable(10*5, np.float32)
網路設定
linear_layer 函式是上面公式的簡單實現,在這裡我們要進行兩個操作:
1.使用times操作對權重W和特徵X進行相乘
2.加上偏差b
feature = C.input_variable(input_dim, np.float32)
# Define a dictionary to store the model parameters
mydict = {}
def linear_layer(input_var, output_dim):
input_dim = input_var.shape[0]
weight_param = C.parameter(shape=(input_dim, output_dim))
bias_param = C.parameter(shape=(output_dim))
mydict['w'], mydict['b'] = weight_param, bias_param
return C.times(input_var, weight_param) + bias_param
output_dim = num_output_classes
z = linear_layer(feature, output_dim)
z用來表示網路的輸出
學習模型引數
現在網路已經建立起來,但是我們想要知道引數W和b,為此我們這裡使用softmax函式,將Z對映到0-1.其中softmax是一個啟用函式,進行歸一化處理。
訓練
通過softmax函式,輸出每個類別的概率。為了訓練分類器,我們需要定義損失函式,最小化輸出和真實標籤的誤差。
H(p)=−∑j=1|y|yjlog(pj)
其中p是經由softmax計算得到的預測概率,y為真實的標籤值。
label = C.input_variable(num_output_classes, np.float32)
loss = C.cross_entropy_with_softmax(z, label)
評估
為了評估分類結果,我們可以計算出classification_error
,如果模型是正確的,則為0,否則為1.
eval_error = C.classification_error(z, label)
訓練
在訓練的過程中,努力是loss最小。在這裡使用隨機梯度下降,SGD。通常,從模型引數的隨機初始化開始。然後計算預測和真實標籤之間的誤差,應用梯度下降生成新的模型引數集合。
# Define a utility function to compute the moving average.
# A more efficient implementation is possible with np.cumsum() function
def moving_average(a, w=10):
if len(a) < w:
return a[:]
return [val if idx < w else sum(a[(idx-w):idx])/w for idx, val in enumerate(a)]
# Define a utility that prints the training progress
def print_training_progress(trainer, mb, frequency, verbose=1):
training_loss, eval_error = "NA", "NA"
if mb % frequency == 0:
training_loss = trainer.previous_minibatch_loss_average
eval_error = trainer.previous_minibatch_evaluation_average
if verbose:
print ("Minibatch: {0}, Loss: {1:.4f}, Error: {2:.2f}".format(mb, training_loss, eval_error))
return mb, training_loss, eval_error
執行訓練模型
經過上述操作,那麼現在我們已經設定好了邏輯迴歸模型。一般我們使用大量的觀察資料進行訓練,比如總資料的70%,剩下的作為評估模型。
# Initialize the parameters for the trainer minibatch_size = 25 num_samples_to_train = 20000 num_minibatches_to_train = int(num_samples_to_train / minibatch_size)
from collections import defaultdict
# Run the trainer and perform model training
training_progress_output_freq = 50
plotdata = defaultdict(list)
for i in range(0, num_minibatches_to_train):
features, labels = generate_random_data_sample(minibatch_size, input_dim, num_output_classes)
# Assign the minibatch data to the input variables and train the model on the minibatch
trainer.train_minibatch({feature : features, label : labels})
batchsize, loss, error = print_training_progress(trainer, i,
training_progress_output_freq, verbose=1)
if not (loss == "NA" or error =="NA"):
plotdata["batchsize"].append(batchsize)
plotdata["loss"].append(loss)
plotdata["error"].append(error)
執行結果為:
Minibatch: 0, Loss: 0.6931, Error: 0.32 Minibatch: 50, Loss: 4.4290, Error: 0.36 Minibatch: 100, Loss: 0.4585, Error: 0.16 Minibatch: 150, Loss: 0.7228, Error: 0.32 Minibatch: 200, Loss: 0.1290, Error: 0.08 Minibatch: 250, Loss: 0.1321, Error: 0.08 Minibatch: 300, Loss: 0.1012, Error: 0.04 Minibatch: 350, Loss: 0.1076, Error: 0.04 Minibatch: 400, Loss: 0.3087, Error: 0.08 Minibatch: 450, Loss: 0.3219, Error: 0.12 Minibatch: 500, Loss: 0.4076, Error: 0.20 Minibatch: 550, Loss: 0.6784, Error: 0.24 Minibatch: 600, Loss: 0.2988, Error: 0.12 Minibatch: 650, Loss: 0.1676, Error: 0.12 Minibatch: 700, Loss: 0.2772, Error: 0.12 Minibatch: 750, Loss: 0.2309, Error: 0.04
# Compute the moving average loss to smooth out the noise in SGD
plotdata["avgloss"] = moving_average(plotdata["loss"])
plotdata["avgerror"] = moving_average(plotdata["error"])
# Plot the training loss and the training error
import matplotlib.pyplot as plt
plt.figure(1)
plt.subplot(211)
plt.plot(plotdata["batchsize"], plotdata["avgloss"], 'b--')
plt.xlabel('Minibatch number')
plt.ylabel('Loss')
plt.title('Minibatch run vs. Training loss')
plt.show()
plt.subplot(212)
plt.plot(plotdata["batchsize"], plotdata["avgerror"], 'r--')
plt.xlabel('Minibatch number')
plt.ylabel('Label Prediction Error')
plt.title('Minibatch run vs. Label Prediction Error')
plt.show()
評估模型
為了評估模型,我們將剩下的資料輸入到已經訓練好的模型中,將真實的結果和預測的結果進行比較。
# Run the trained model on a newly generated dataset
test_minibatch_size = 25
features, labels = generate_random_data_sample(test_minibatch_size, input_dim, num_output_classes)
trainer.test_minibatch({feature : features, label : labels})
此時,這裡的minibatch為0.12,這是一個關鍵的指標,如果錯誤大大超過的訓練誤差,則表明訓練後的模型在訓練過程中出現了過擬合情況。
預測評估
檢視預測錯誤的個數
print("Label :", [np.argmax(label) for label in labels])
print("Predicted:", [np.argmax(x) for x in result[0]])
視覺化
# Model parameters
print(mydict['b'].value)
bias_vector = mydict['b'].value
weight_matrix = mydict['w'].value
# Plot the data
import matplotlib.pyplot as plt
# let 0 represent malignant/red, and 1 represent benign/blue
colors = ['r' if label == 0 else 'b' for label in labels[:,0]]
plt.scatter(features[:,0], features[:,1], c=colors)
plt.plot([0, bias_vector[0]/weight_matrix[0][1]],
[ bias_vector[1]/weight_matrix[0][0], 0], c = 'g', lw = 3)
plt.xlabel("Patient age (scaled)")
plt.ylabel("Tumor size (in cm)")
plt.show()