機器學習(迴歸二)——線性迴歸-最小二乘-代碼實現

本篇內容本來想在寫在上篇博客中的,那樣篇幅過長,就單獨提出來了。

機器學習中經常用到scikit-learn,他是一個建立在Scipy基礎上的用於機器學習的Python模塊。在不同的應用領域中,已經發展出爲數衆多的基於Scipy的工具包,他們統稱爲Scikits。而在所有的分支版本中,scikit-learn是最有名的,是開源的,任何人都可以免費地使用這個庫或者進行二次開發。

scikit-learn包含衆多頂級機器學習算法,主要有六大基本功能,分別是分類、迴歸、聚類、數據降維、模型選擇和數據預處理。scikit-learn擁有非常活躍的用戶社區,基本上其所有的功能都有非常詳盡的文檔供用戶查閱。可以研讀scikit-learn的用戶指南及文檔,對其算法的使用有更充分的瞭解。

本篇文章採用兩種方式實現線性迴歸,一種是使用scikit-learn。而通過上篇博客,我們已經知道了最小二乘法求解線性迴歸參數,所以完全可以自己手動實現。

一、使用scikit-learn

API

使用到的線性迴歸的API::
在這裏插入圖片描述
而該API內部實現就是使用的普通最小二乘法。

功能

現有一批描述家庭用電情況的數據,對數據進行算法模型預測,並最終得到預測模型(每天各個時間段和功率之間的關係、功率與電流之間的關係等)。數據來源:Individual household electric power consumption Data Set,點擊Data Folder -->household_power_consumption.zip 下載即可。或者去這個地方下載(本人已上傳資源,0C幣) “household_power_consumption_1000.zip
在這裏插入圖片描述

代碼

代碼是基於“jupyter notebook”環境下的,爲了知道代碼的一些中間環節,代碼會中斷來展示一些中間數據、圖形等。

#引入線性迴歸的API
from sklearn.model_selection import train_test_split # 數據劃分的類
from sklearn.linear_model import LinearRegression # 線性迴歸的類
from sklearn.preprocessing import StandardScaler # 數據標準化

# 引入其他所需要的全部包
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
from pandas import DataFrame
import time

# 解決中文顯示問題
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False

# 加載數據
# 日期、時間、有功功率、無功功率、電壓、電流、廚房用電功率、洗衣服用電功率、熱水器用電功率
path1='datas/household_power_consumption_1000.txt'
df = pd.read_csv(path1, sep=';', low_memory=False)#沒有混合類型的時候可以通過low_memory=F調用更多內存,加快效率)
df.head() ## 獲取前五行數據查看查看

前五行數據

# 查看格式信息
df.info()

在這裏插入圖片描述
數據中會有一些異常數據,所以需要處理一下:
在這裏插入圖片描述
通過上圖會知道有缺失的數據。

# 異常數據處理(異常數據過濾)
new_df = df.replace('?', np.nan) # 替換非法字符爲np.nan
datas = new_df.dropna(axis=0, how = 'any') # 只要有一個數據爲空,就進行行刪除操作
datas.describe().T # 觀察數據的多種統計指標(只能看數值型的)

在這裏插入圖片描述

## 創建一個時間函數格式化字符串
def date_format(dt):
    # dt顯示是一個series/tuple;dt[0]是date,dt[1]是time
	# import time
    t = time.strptime(' '.join(dt), '%d/%m/%Y %H:%M:%S')
    return (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)

## 需求:構建時間和功率之間的映射關係,可以認爲:特徵屬性爲時間;目標屬性爲功率值。
# 獲取x和y變量, 並將時間轉換爲數值型連續變量
X = datas.iloc[:,0:2]
X = X.apply(lambda x: pd.Series(date_format(x)), axis=1)
Y = datas['Global_active_power']

X.head(2)

在這裏插入圖片描述

## 對數據集進行測試集合訓練集劃分
# X:特徵矩陣(類型一般是DataFrame)
# Y:特徵對應的Label標籤(類型一般是Series)
# test_size: 對X/Y進行劃分的時候,測試集合的數據佔比, 是一個(0,1)之間的float類型的值
# random_state: 數據分割是基於隨機器進行分割的,該參數給定隨機數種子;給一個值(int類型)的作用就是保證每次分割所產生的數數據集是完全相同的
X_train,X_test,Y_train,Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)

print(X_train.shape)
print(X_test.shape)
print(Y_train.shape)

在這裏插入圖片描述

# 查看訓練集上的數據信息(X)
X_train.describe()

在這裏插入圖片描述

## 數據標準化
# StandardScaler:將數據轉換爲標準差爲1的數據集(有一個數據的映射)
# scikit-learn中:如果一個API名字有fit,那麼就有模型訓練的含義,沒法返回值
# scikit-learn中:如果一個API名字中有transform, 那麼就表示對數據具有轉換的含義操作
# scikit-learn中:如果一個API名字中有predict,那麼就表示進行數據預測,會有一個預測結果輸出
# scikit-learn中:如果一個API名字中既有fit又有transform的情況下,那就是兩者的結合(先做fit,再做transform)
ss = StandardScaler() # 模型對象創建
X_train = ss.fit_transform(X_train) # 訓練模型並轉換訓練集
X_test = ss.transform(X_test) ## 直接使用在模型構建數據上進行一個數據標準化操作 (測試集)

pd.DataFrame(X_train).describe()

在這裏插入圖片描述

## 模型訓練
lr = LinearRegression(fit_intercept=True) # 模型對象構建
'''
LinearRegression(fit_intercept=True, normalize=False,copy_X=True,n_jobs=1)
	fit_intercept:是否需要截距
	normalize:是否做標準化,上面已在單拿出來做了標準化
	copy_X:是否進行數據複製,如果複製了,對數據進行修改,就不會改變原數據
	n_jobs:並行運行。但需要CPU至少雙核,基本不怎麼用
'''
lr.fit(X_train, Y_train) ## 訓練模型
## 模型校驗
y_predict = lr.predict(X_test) ## 預測結果

print("訓練集上R2:",lr.score(X_train, Y_train))
print("測試集上R2:",lr.score(X_test, Y_test))
mse = np.average((y_predict-Y_test)**2)
rmse = np.sqrt(mse)
print("rmse:",rmse)

在這裏插入圖片描述
這裏的R²可以當成準確率,後續的文章中會有詳細介紹。

# 輸出模型訓練得到的相關參數
print("模型的係數(θ):", end="")
print(lr.coef_)
print("模型的截距:", end='')
print(lr.intercept_)

在這裏插入圖片描述
θ中的第1,2,6個爲0,說明這一維度的數據對於模型而言不起作用。觀察上面X_train.describe()輸出的數據,發現這幾維度的數據的方差都爲0,說明這幾個維度的數據是一樣的,肯定對模型而言,起不了作用。

## 模型保存/持久化
# 在機器學習部署的時候,實際上其中一種方式就是將模型進行輸出;另外一種方式就是直接將預測結果輸出
# 模型輸出一般是將模型輸出到磁盤文件
from sklearn.externals import joblib

# 保存模型要求給定的文件所在的文件夾比較存在
 joblib.dump(ss, "result/data_ss.model") ## 將標準化模型保存
 joblib.dump(lr, "result/data_lr.model") ## 將模型保存

在這裏插入圖片描述

# 加載模型
ss3 = joblib.load("result/data_ss.model") ## 加載模型
lr3 = joblib.load("result/data_lr.model") ## 加載模型

# 使用加載的模型進行預測
data1 = [[2006, 12, 17, 12, 25, 0]]
data1 = ss.transform(data1)
print(data1)
lr.predict(data1)

在這裏插入圖片描述

## 預測值和實際值畫圖比較
t=np.arange(len(X_test))
plt.figure(facecolor='w')#建一個畫布,facecolor是背景色
plt.plot(t, Y_test, 'r-', linewidth=2, label='真實值')
plt.plot(t, y_predict, 'g-', linewidth=2, label='預測值')
plt.legend(loc = 'upper left')#顯示圖例,設置圖例的位置
plt.title("線性迴歸預測時間和功率之間的關係", fontsize=20)
plt.grid(b=True)#加網格
plt.show()

在這裏插入圖片描述

## 功率和電流之間的關係
X = datas.iloc[:,2:4]
Y2 = datas.iloc[:,5]

## 數據分割
X2_train,X2_test,Y2_train,Y2_test = train_test_split(X, Y2, test_size=0.2, random_state=0)

## 數據歸一化
scaler2 = StandardScaler()
X2_train = scaler2.fit_transform(X2_train) # 訓練並轉換
X2_test = scaler2.transform(X2_test) ## 直接使用在模型構建數據上進行一個數據標準化操作 

## 模型訓練
lr2 = LinearRegression()
lr2.fit(X2_train, Y2_train) ## 訓練模型

## 結果預測
Y2_predict = lr2.predict(X2_test)

## 模型評估
print("電流預測準確率: ", lr2.score(X2_test,Y2_test))
print("電流參數:", lr2.coef_)

## 繪製圖表
#### 電流關係
t=np.arange(len(X2_test))
plt.figure(facecolor='w')
plt.plot(t, Y2_test, 'r-', linewidth=2, label=u'真實值')
plt.plot(t, Y2_predict, 'g-', linewidth=2, label=u'預測值')
plt.legend(loc = 'lower right')
plt.title(u"線性迴歸預測功率與電流之間的關係", fontsize=20)
plt.grid(b=True)
plt.show()

在這裏插入圖片描述
【總結】
發現使用scikitlearn做機器學習特別簡單:

  • 首先考慮做什麼事,如我要做時間和功率之間的映射關係
  • 然後找相關的數據:時間是什麼、功率是什麼
  • 找到數據,把數據分成兩塊:訓練集、測試集
  • 對數據進行一些操作:格式化、異常值處理、標準化
  • 接下來做模型的訓練

就一個模型來講,流程就是這些。

二、自己實現

對於最小二乘法已經瞭解了,代碼實現的過程也知道。我們完全可以自己通過代碼實現最小二乘法,來進行預測。

# 引入所需要的全部包
from sklearn.model_selection import train_test_split # 數據劃分的類

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
from pandas import DataFrame
import time

## 設置字符集,防止中文亂碼
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcParams['axes.unicode_minus']=False

# 加載數據
# 日期、時間、有功功率、無功功率、電壓、電流、廚房用電功率、洗衣服用電功率、熱水器用電功率
path1='datas/household_power_consumption_1000.txt'
df = pd.read_csv(path1, sep=';', low_memory=False)#沒有混合類型的時候可以通過low_memory=F調用更多內存,加快效率)

df.head(2)

在這裏插入圖片描述

## 功率和電流之間的關係
X = df.iloc[:,2:4]
Y2 = df.iloc[:,5]

## 數據分割
X2_train,X2_test,Y2_train,Y2_test = train_test_split(X, Y2, test_size=0.2, random_state=0)

type(X2_train)

在這裏插入圖片描述
發現不是矩陣,必須轉換成矩陣才能進行最小二乘公式計算。

# 將X和Y轉換爲矩陣的形式
X = np.mat(X2_train)
Y = np.mat(Y2_train).reshape(-1,1)

type(X)

在這裏插入圖片描述
此時發現,已經轉成矩陣。

# 計算θ
theta = (X.T * X).I * X.T * Y
print(theta)

在這裏插入圖片描述
用到的就是我們上篇博客中最小二乘法得到的解析式:minθJ(θ)=(XTX)1XTy\min\limits_{\bm{\theta}} J(\theta) = \left( X^T X \right)^{-1} X^T \bm{y}

# 對測試集合進行測試
y_hat = np.mat(X2_test) * theta
# 畫圖看看
#### 電流關係
t=np.arange(len(X2_test))
plt.figure(facecolor='w')
plt.plot(t, Y2_test, 'r-', linewidth=2, label=u'真實值')
plt.plot(t, y_hat, 'g-', linewidth=2, label=u'預測值')
plt.legend(loc = 'lower right')
plt.title(u"線性迴歸預測功率與電流之間的關係", fontsize=20)
plt.grid(b=True)
plt.show()

在這裏插入圖片描述
運行代碼,發現運行效果跟scikitlearn的運行效果差不多。

最後,再總結一下: 其實很簡單,除去畫圖的代碼,主要部分就剩下數據處理和訓練那幾行代碼。好了,掌聲,起!
在這裏插入圖片描述

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