1. 程式人生 > >Tensorflow搭建第一個邏輯迴歸(logistic regression,其實也就是單層感知機)模型

Tensorflow搭建第一個邏輯迴歸(logistic regression,其實也就是單層感知機)模型

資料集:

使用吳恩達機器學習課程:https://study.163.com/course/courseMain.htm?courseId=1004570029

章節8中的課時60:程式設計作業:Logistic迴歸的資料中的資料集ex2data1.txt(訓練集)和ex2data2.txt(測試集),下載地址:

我們的任務是:根據測試集學生已知的兩次考試成績和最後的錄取結果訓練一個模型,然後判斷測試集中的學生樣本是否會被錄取。

搭建第一個邏輯迴歸(logistic regression,也就是單層感知機)模型:

感知器: 

感知器神經網路是一種典型的前饋神經網路,具有分層結果,資訊叢輸入層進入網路,逐層向前傳遞至輸出層。根據感知器神經元轉移函式、隱層數以及權值調整規則的不同,可以形成具有各種功能特點的神經網路。

單層感知器:

一種具有單層計算單元的神經網路,即感知器(Perceptron)。如果包括輸入層在內,應為兩層,即輸入層和輸出層。單層感知器的結構和功能非常簡單,以至於目前在解決實際問題時很少被採用,但由於它是神經網路的基礎,適合作為神經網路學習的起點。

單層感知器的結構:

我們可以發現,logistic regression函式實際上就是一個最簡單的單層感知器。

當然,單層感知器還可以有其他形式,如:

左邊是一個單層單輸出感知器,右邊是一個單層多輸出感知器。

單層感知器有一個缺點,即它們只能對線性可分的資料集進行分類。

比如下面這樣的資料可以被單層感知器區分:

建立logistic regression(即單層感知機)模型:

我們將ex2data1.txt和ex2data2.txt與我們的模型的.py檔案放到同一目錄下。

首先處理訓練資料集,將訓練資料集的x資料規整為shape=(100,2)的陣列,y資料規整為shape=(100,1)的陣列;

定義取得w和b變數的函式;

對x資料進行z_score標準化,否則進行sigmoid計算時資料會溢位;

定義X和Y佔位符,用於將輸入的x和y資料轉化相應shape的張量;

定義w和b變數,定義logistic regression函式;

新建一個訓練對話,迭代訓練20000次;

處理測試資料集的資料,方法同對訓練資料集一樣,並對測試資料集的x資料進行z_score標準化;

新建一個測試對話,計算測試的準確率並輸出。

佔位符placeholder:

即動態分配了一塊“空間”出來給placeholder型別的變數,空間指定了其內容的大小和型別,而裡面填充什麼內容需要經過之後的處理才知道。

Feed--Fetch機制:

feed使用一個tensor值臨時替換一個操作的輸出結果。通俗地說,就是把神經網路的結構與資料分開。對於一個節點,我們可以事先通過placeholder開闢一塊空間,然後在session中給它“喂”資料。使用的方法是Python的字典。方法結束後feed就會消失。

程式碼如下:

import tensorflow as tf
from math import sqrt, pow
import csv
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
os.environ["CUDA_VISIBLE_DEVICES"] = "0"

train_x_data_set = []
train_y_data_set = []

# 從ex2data1.txt中提取出用於train的x資料集和y資料集
with open('ex2data1.txt', 'r', encoding='UTF-8', errors='ignore') as csv_file:
	all_lines = csv.reader(csv_file)
	# 遍歷train.csv的所有行
	for one_line in all_lines:
		one_line_x_data = []
		one_line_y_data = []
		for i, element in enumerate(one_line):
			# 去除字串首尾的空格
			element = element.strip()
			if i == 2:
				one_line_y_data.append(float(element))
			else:
				one_line_x_data.append(float(element))
		train_x_data_set.append(one_line_x_data)
		train_y_data_set.append(one_line_y_data)


# 得到的train_x_data_set的shape為(100,2),train_y_data_set的shape為(100,1)

# 測試
# print(train_x_data_set)
# print(train_y_data_set)

# 定義weight變數
def weight_variable(shape):
	inital = tf.truncated_normal(shape, stddev=0.1)
	# tf.truncated_normal(shape, mean, stddev)函式從截斷的正態分佈中輸出隨機值。均值和標準差自己設定。
	return tf.Variable(inital)


# 定義bias變數
def bias_variable(shape):
	initial = tf.constant(0.1, shape=shape)
	return tf.Variable(initial)


# 對資料作z_score標準化處理,直接計算時sigmoid函式計算會溢位
def data_z_score_standardization(x_data_set):
	# 對x_data_set中所有的資料都進行歸一化
	for index in range(len(x_data_set[0])):
		# 取出需要歸一化的一組資料
		data_list = []
		for one_data in x_data_set:
			data_list.append(one_data[index])
		# 計算這組資料的平均值
		data_mean = 0.0
		for data in data_list:
			data_mean = data_mean + data
		data_mean = data_mean / len(data_list)
		# 計算這組資料的方差
		data_variance = 0.0
		for data in data_list:
			data_variance = data_variance + pow((data - data_mean), 2)
		data_variance = data_variance / len(data_list)
		# 計算這組資料的標準差
		data_standard_deviation = sqrt(data_variance)
		# 將train_x_data_set的相關資料標準化
		for subscript, one_data in enumerate(x_data_set):
			x_data_set[subscript][index] = (one_data[index] - data_mean) / data_standard_deviation
	return x_data_set


# 對train_x_data_set做z_score標準化
train_x_data_set = data_z_score_standardization(train_x_data_set)

# 用X和Y佔位符來傳輸訓練集的x資料和y資料(把輸入的資料變成指定shape的張量)
X = tf.placeholder(tf.float32, [None, 2])
Y = tf.placeholder(tf.float32, [None, 1])
iteration = 20000

# 生成w和b,計算y_pred
w_train = tf.Variable(weight_variable([2, 1]))
b_train = tf.Variable(bias_variable([1]))
y_pred = 1 / (1 + tf.exp(-(tf.matmul(X, w_train) + b_train)))

cross_entropy = tf.reduce_mean(- tf.reduce_sum((Y * tf.log(y_pred) + (1 - Y) * tf.log(1 - y_pred)), 1))
optimizer = tf.train.GradientDescentOptimizer(0.005).minimize(cross_entropy)

saver = tf.train.Saver(max_to_keep=5)
# 建立儲存模型的路徑
if not os.path.exists("./tmp/"):
	os.mkdir("./tmp")
with tf.Session() as sess_train:
	sess_train.run(tf.global_variables_initializer())
	if os.path.exists("./tmp/checkpoint"):
		# 判斷模型是否存在,如果存在則從模型中恢復變數
		saver.restore(sess_train, tf.train.latest_checkpoint('./tmp/'))
	for i in range(iteration):
		# 注意feed_dict不接受tensor張量,但可以是np的數列或python的列表等
		_, loss = sess_train.run([optimizer, cross_entropy],
								 feed_dict={X: train_x_data_set, Y: train_y_data_set})
		w, b = sess_train.run([w_train, b_train], feed_dict={X: train_x_data_set, Y: train_y_data_set})
		w, b = w.flatten(), b.flatten()
		if i % 20 == 0:
			print("iteration:{} loss值:{} w值:{} b值:{}".format(i, loss, w, b))
		if i % 100 == 0:
			saver.save(sess_train, "./tmp/train_model", global_step=i)
		if loss <= 0.205:
			break

test_x_data_set = []
test_y_data_set = []

# 從ex2data1.txt中提取出用於train的x資料集和y資料集
with open('ex2data2.txt', 'r', encoding='UTF-8', errors='ignore') as csv_file:
	all_lines = csv.reader(csv_file)
	# 遍歷train.csv的所有行
	for one_line in all_lines:
		one_line_x_data = []
		one_line_y_data = []
		for i, element in enumerate(one_line):
			# 去除字串首尾的空格
			element = element.strip()
			if i == 2:
				one_line_y_data.append(float(element))
			else:
				one_line_x_data.append(float(element))
		test_x_data_set.append(one_line_x_data)
		test_y_data_set.append(one_line_y_data)

# 得到的test_x_data_set的shape為(100,2),test_y_data_set的shape為(100,1)
# 對test_x_data_set做z_score標準化
test_x_data_set = data_z_score_standardization(test_x_data_set)

with tf.Session() as sess_test:
	sess_test.run(tf.global_variables_initializer())
	if os.path.exists("./tmp/checkpoint"):
		# 直接恢復成訓練好的模型,然後開始測試
		saver.restore(sess_test, tf.train.latest_checkpoint('./tmp/'))
	y_out, y_true, loss = sess_test.run([y_pred, Y, cross_entropy], feed_dict={X: test_x_data_set, Y: test_y_data_set})
	# 生成的y_out是shape=(test樣本數量,1)的np陣列,每個元素在(0,1)之間
	# 我們判斷陣列中每個元素的值,如果大於等於0.5,則y_out對應值是1.0(類1),否則是0.0(類0)
	for i in range(len(y_out)):
		if y_out[i][0] >= 0.5:
			y_out[i][0] = 1.0
		else:
			y_out[i][0] = 0.0
	# 將修改好的列表重新轉為張量y_prediction
	for i in range(len(y_out)):
		print("第{}個樣本的預測標籤為:{} 真實標籤為:{}".format(i, y_out[i][0], y_true[i][0]))
	y_prediction = tf.reshape(y_out, [-1, 1])
	# 判斷張量y_prediction和Y對應元素是否相等,相等則返回True,否則返回False,correct_pre是一個shape=(100,1)的張量
	# 該張量中元素值要麼是True,要麼是False
	correct_pre = tf.equal(y_prediction, Y)
	# cast函式會將correct_pre張量中元素值為True的變為值1,元素值為False的變為值0,然後求平均值,就是準確率
	accuracy = tf.reduce_mean(tf.cast(correct_pre, tf.float32))
	acc = sess_test.run(accuracy, feed_dict={X: test_x_data_set, Y: test_y_data_set})
	print("測試集loss值為:{} 準確率為:{}".format(loss, acc))

執行結果如下:

訓練過程就不放了,最後訓練到loss值小於0.205停止,然後開始測試資料:

第0個樣本的預測標籤為:1.0 真實標籤為:1.0
第1個樣本的預測標籤為:1.0 真實標籤為:1.0
第2個樣本的預測標籤為:1.0 真實標籤為:1.0
第3個樣本的預測標籤為:1.0 真實標籤為:1.0
第4個樣本的預測標籤為:0.0 真實標籤為:1.0
第5個樣本的預測標籤為:0.0 真實標籤為:1.0
第6個樣本的預測標籤為:0.0 真實標籤為:1.0
第7個樣本的預測標籤為:0.0 真實標籤為:1.0
第8個樣本的預測標籤為:0.0 真實標籤為:1.0
第9個樣本的預測標籤為:0.0 真實標籤為:1.0
第10個樣本的預測標籤為:0.0 真實標籤為:1.0
第11個樣本的預測標籤為:1.0 真實標籤為:1.0
第12個樣本的預測標籤為:1.0 真實標籤為:1.0
第13個樣本的預測標籤為:1.0 真實標籤為:1.0
第14個樣本的預測標籤為:1.0 真實標籤為:1.0
第15個樣本的預測標籤為:1.0 真實標籤為:1.0
第16個樣本的預測標籤為:1.0 真實標籤為:1.0
第17個樣本的預測標籤為:1.0 真實標籤為:1.0
第18個樣本的預測標籤為:1.0 真實標籤為:1.0
第19個樣本的預測標籤為:1.0 真實標籤為:1.0
第20個樣本的預測標籤為:0.0 真實標籤為:1.0
第21個樣本的預測標籤為:0.0 真實標籤為:1.0
第22個樣本的預測標籤為:0.0 真實標籤為:1.0
第23個樣本的預測標籤為:0.0 真實標籤為:1.0
第24個樣本的預測標籤為:0.0 真實標籤為:1.0
第25個樣本的預測標籤為:0.0 真實標籤為:1.0
第26個樣本的預測標籤為:0.0 真實標籤為:1.0
第27個樣本的預測標籤為:1.0 真實標籤為:1.0
第28個樣本的預測標籤為:1.0 真實標籤為:1.0
第29個樣本的預測標籤為:1.0 真實標籤為:1.0
第30個樣本的預測標籤為:1.0 真實標籤為:1.0
第31個樣本的預測標籤為:1.0 真實標籤為:1.0
第32個樣本的預測標籤為:1.0 真實標籤為:1.0
第33個樣本的預測標籤為:1.0 真實標籤為:1.0
第34個樣本的預測標籤為:0.0 真實標籤為:1.0
第35個樣本的預測標籤為:0.0 真實標籤為:1.0
第36個樣本的預測標籤為:0.0 真實標籤為:1.0
第37個樣本的預測標籤為:0.0 真實標籤為:1.0
第38個樣本的預測標籤為:0.0 真實標籤為:1.0
第39個樣本的預測標籤為:0.0 真實標籤為:1.0
第40個樣本的預測標籤為:1.0 真實標籤為:1.0
第41個樣本的預測標籤為:1.0 真實標籤為:1.0
第42個樣本的預測標籤為:1.0 真實標籤為:1.0
第43個樣本的預測標籤為:1.0 真實標籤為:1.0
第44個樣本的預測標籤為:1.0 真實標籤為:1.0
第45個樣本的預測標籤為:1.0 真實標籤為:1.0
第46個樣本的預測標籤為:1.0 真實標籤為:1.0
第47個樣本的預測標籤為:1.0 真實標籤為:1.0
第48個樣本的預測標籤為:1.0 真實標籤為:1.0
第49個樣本的預測標籤為:1.0 真實標籤為:1.0
第50個樣本的預測標籤為:1.0 真實標籤為:1.0
第51個樣本的預測標籤為:1.0 真實標籤為:1.0
第52個樣本的預測標籤為:1.0 真實標籤為:1.0
第53個樣本的預測標籤為:1.0 真實標籤為:1.0
第54個樣本的預測標籤為:0.0 真實標籤為:1.0
第55個樣本的預測標籤為:0.0 真實標籤為:1.0
第56個樣本的預測標籤為:0.0 真實標籤為:1.0
第57個樣本的預測標籤為:0.0 真實標籤為:1.0
第58個樣本的預測標籤為:1.0 真實標籤為:0.0
第59個樣本的預測標籤為:1.0 真實標籤為:0.0
第60個樣本的預測標籤為:1.0 真實標籤為:0.0
第61個樣本的預測標籤為:1.0 真實標籤為:0.0
第62個樣本的預測標籤為:1.0 真實標籤為:0.0
第63個樣本的預測標籤為:1.0 真實標籤為:0.0
第64個樣本的預測標籤為:1.0 真實標籤為:0.0
第65個樣本的預測標籤為:1.0 真實標籤為:0.0
第66個樣本的預測標籤為:1.0 真實標籤為:0.0
第67個樣本的預測標籤為:1.0 真實標籤為:0.0
第68個樣本的預測標籤為:1.0 真實標籤為:0.0
第69個樣本的預測標籤為:1.0 真實標籤為:0.0
第70個樣本的預測標籤為:1.0 真實標籤為:0.0
第71個樣本的預測標籤為:1.0 真實標籤為:0.0
第72個樣本的預測標籤為:1.0 真實標籤為:0.0
第73個樣本的預測標籤為:1.0 真實標籤為:0.0
第74個樣本的預測標籤為:0.0 真實標籤為:0.0
第75個樣本的預測標籤為:0.0 真實標籤為:0.0
第76個樣本的預測標籤為:1.0 真實標籤為:0.0
第77個樣本的預測標籤為:0.0 真實標籤為:0.0
第78個樣本的預測標籤為:0.0 真實標籤為:0.0
第79個樣本的預測標籤為:0.0 真實標籤為:0.0
第80個樣本的預測標籤為:0.0 真實標籤為:0.0
第81個樣本的預測標籤為:0.0 真實標籤為:0.0
第82個樣本的預測標籤為:0.0 真實標籤為:0.0
第83個樣本的預測標籤為:0.0 真實標籤為:0.0
第84個樣本的預測標籤為:0.0 真實標籤為:0.0
第85個樣本的預測標籤為:0.0 真實標籤為:0.0
第86個樣本的預測標籤為:0.0 真實標籤為:0.0
第87個樣本的預測標籤為:0.0 真實標籤為:0.0
第88個樣本的預測標籤為:1.0 真實標籤為:0.0
第89個樣本的預測標籤為:1.0 真實標籤為:0.0
第90個樣本的預測標籤為:1.0 真實標籤為:0.0
第91個樣本的預測標籤為:1.0 真實標籤為:0.0
第92個樣本的預測標籤為:1.0 真實標籤為:0.0
第93個樣本的預測標籤為:1.0 真實標籤為:0.0
第94個樣本的預測標籤為:1.0 真實標籤為:0.0
第95個樣本的預測標籤為:1.0 真實標籤為:0.0
第96個樣本的預測標籤為:1.0 真實標籤為:0.0
第97個樣本的預測標籤為:1.0 真實標籤為:0.0
第98個樣本的預測標籤為:1.0 真實標籤為:0.0
第99個樣本的預測標籤為:1.0 真實標籤為:0.0
第100個樣本的預測標籤為:1.0 真實標籤為:0.0
第101個樣本的預測標籤為:1.0 真實標籤為:0.0
第102個樣本的預測標籤為:0.0 真實標籤為:0.0
第103個樣本的預測標籤為:0.0 真實標籤為:0.0
第104個樣本的預測標籤為:0.0 真實標籤為:0.0
第105個樣本的預測標籤為:0.0 真實標籤為:0.0
第106個樣本的預測標籤為:0.0 真實標籤為:0.0
第107個樣本的預測標籤為:0.0 真實標籤為:0.0
第108個樣本的預測標籤為:0.0 真實標籤為:0.0
第109個樣本的預測標籤為:0.0 真實標籤為:0.0
第110個樣本的預測標籤為:0.0 真實標籤為:0.0
第111個樣本的預測標籤為:0.0 真實標籤為:0.0
第112個樣本的預測標籤為:0.0 真實標籤為:0.0
第113個樣本的預測標籤為:0.0 真實標籤為:0.0
第114個樣本的預測標籤為:0.0 真實標籤為:0.0
第115個樣本的預測標籤為:1.0 真實標籤為:0.0
第116個樣本的預測標籤為:1.0 真實標籤為:0.0
第117個樣本的預測標籤為:1.0 真實標籤為:0.0
測試集loss值為:2.23197078704834 準確率為:0.508474588394165

Process finished with exit code 0

我們可以發現準確率非常低,和蒙差不多。這是因為這組資料集的資料分佈的非常均勻,無法用線性函式區分,而單層感知器只能用來區分線性可分的資料。因此這個資料集我們應該用更加複雜的神經網路結構來進行預測。