當我們爬完和分析完資料後,可不可以做更進一步的應用呢?
sure! 接下來就要來預測某篇post的按讚程度,假設我們知道post內容,發文類型,發文時間點,
我們能不能預測這篇post能得到多少讚呢? 若要預測得到多少讚的話,這就是一個regression problem
但我們先簡單一點,把它當成一個classification problem,
要如何作到這一點呢?
我們把reaction高於中位數的label成1,低於的label成0,去預測某篇post是0或是1就行了
In [1]:
import pandas as pd
import operator
import datetime
import numpy as np
一樣先把資料讀進來
In [2]:
page_id = "appledaily.tw"
path = 'post/'+page_id+'_post.csv'
df = pd.read_csv(path, encoding='utf8')
看一看前五筆
In [3]:
df.head()
Out[3]:
In [4]:
len(df)
Out[4]:
一共5234筆發文
In [5]:
df.apply(lambda x: sum(x.isnull()))
Out[5]:
把status_message為空值的地方拿掉
In [6]:
df = df[df['status_message'].notnull()].reindex()
再把reactions等於0的去掉
In [7]:
df = df[df['num_reactions']!=0].reindex()
In [8]:
len(df)
Out[8]:
剩下5061篇發文
一樣處理時間,新增星期幾和單日第幾時po文的欄位
In [9]:
df['datetime'] = df['status_published'].apply(lambda x: datetime.datetime.strptime(x,'%Y-%m-%d %H:%M:%S'))
df['weekday'] = df['datetime'].apply(lambda x: x.weekday_name)
df['hour'] = df['datetime'].apply(lambda x: x.hour)
status_type,weekday必須經過One-Hot Coding的處理,因為
假設有一個feature是 地區:["Europe","US","Asia"],我們先把它數字化[2,1,0]
但轉成數字後就存在1>0,2>0這種關係,但是US不會大於Asia,所以還必須經過別種處理方式
要把一維的地區feature轉成三維的地區feature,變成是否就可以用[1,0]表示
In [10]:
pd.Series(data = ["EuropeS","Asia","Europe","US","Asia"], name = 'location')
Out[10]:
In [11]:
# Numerical Coding
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit_transform(pd.Series(data = ["Europe","Asia","Europe","US","Asia"], name = 'location'))
Out[11]:
In [12]:
# One-Hot Coding
pd.get_dummies(le.fit_transform(pd.Series(data = ["Europe","Asia","Europe","US","Asia"], name = 'location')))
Out[12]:
In [13]:
var_to_encode = ['status_type','weekday']
for col in var_to_encode:
df[col] = le.fit_transform(df[col])
df = pd.get_dummies(df, columns=var_to_encode+['hour'])
In [14]:
y = df['num_reactions']
In [15]:
y.describe()
Out[15]:
In [16]:
y = df['num_reactions'].apply(lambda x: 1 if x > 1243 else 0)
In [17]:
# 載入繁體詞庫
import jieba.analyse
# 安裝jieba套件的時候,就有繁體詞庫
jieba.set_dictionary('/home/wy/anaconda3/envs/python3/lib/python3.6/site-packages/jieba/extra_dict/dict.txt.big')
def jieba_extract(message_list):
word_count = {}
for message in message_list:
seg_list = jieba.analyse.extract_tags(message, topK=120)
for seg in seg_list:
if not seg in word_count:
word_count[seg] = 1
else:
word_count[seg] += 1
sorted_word_count = sorted(word_count.items(), key=operator.itemgetter(1))
sorted_word_count.reverse()
return sorted_word_count
In [18]:
sorted_word_count = jieba_extract(list(df['status_message']))
word = [one[0] for one in sorted_word_count]
In [19]:
def get_word_vector(message):
v = np.zeros(len(word))
v_word = jieba.analyse.extract_tags(message, topK=120)
for w in v_word:
v[words.index(w)] = 1
return v
In [20]:
sorted_word_count = jieba_extract(list(df['status_message']))
words = [one[0] for one in sorted_word_count]
word_vector = [get_word_vector(one) for one in df['status_message']]
word_vector = np.array(word_vector)
In [21]:
word_vector.shape
word_vector
Out[21]:
代表有5061篇發文和27518個詞
In [22]:
df.columns
Out[22]:
看一下資料的欄位,我們只能使用status_message,status_type,weekday,hour
因為 num_reactions 和剩下的欄位都有關係(分享數多那按讚也會多 blabla)所以不能拿進來用
status_type,weekday,hour 已經經過One-Hot Coding處理,才會有這麼多欄位
In [23]:
features = df[['status_type_0', 'status_type_1',
'status_type_2', 'status_type_3', 'weekday_0', 'weekday_1', 'weekday_2',
'weekday_3', 'weekday_4', 'weekday_5', 'weekday_6', 'hour_0', 'hour_1',
'hour_2', 'hour_3', 'hour_4', 'hour_5', 'hour_6', 'hour_7', 'hour_8',
'hour_9', 'hour_10', 'hour_11', 'hour_12', 'hour_13', 'hour_14',
'hour_15', 'hour_16', 'hour_17', 'hour_18', 'hour_19', 'hour_20',
'hour_21', 'hour_22', 'hour_23']].as_matrix()
features
Out[23]:
把詞向量的feature和其他feature結合在一起
In [24]:
X = np.concatenate((features, word_vector), axis=1)
In [25]:
X
Out[25]:
處理後的feature 維度和數量
In [26]:
X.shape
Out[26]:
處理後的label 數量
In [27]:
y.shape
Out[27]:
接下剌我們要使用的model是logistic regression
logistic regression : 是機器學習分類算法的其中一,其算法作為一個二分類算法,主要解決的是線性可分的問題
方程式 : $$ h(x)=\frac{1}{1+exp(-w^Tx)} $$
假設我們可以知道一個病人的性別,年齡,身高,體重,血壓...為了要計算他得到癌症的機率,可以將這幾個特徵做加權求和
x代表每個特徵,w代表每個特徵相對應的權重,s為加權求和的值
$$
\color{purple}{s}=\sum_{i={0}}^d{w_i}{x_i}=w^Tx
$$
但是算出來的s範圍是負無限大到正無限大,我們會希望得到一個機率估計的值0到1,所以要把s經過sigmoid函數${\theta}$處理
$$
{\theta}({s})=\frac{e^{s}}{1+e^{s}}=\frac{1}{1+e^{-s}}
$$
綜合以上由於我們只要可以知道x和w,就可以進行預測了!
x是我們已經知道的,但是要如何得到w呢?
我們先需要也要有一個可以衡量這組w好不好的函數稱為cost function
這邊所使用的誤差函數為 cross entropy error (直接給方程式,就不附推導過程)
y為train data中的label,1就是得癌症,0就是沒得
$$
err({w},x,y)=ln(1+exp(-y{w}x))
$$
所以cost function為 $$ E({w})=\frac{1}{N}\sum_{n=1}^{N}ln(1+exp(-y_n{w}^Tx_n)) $$
有了cost function就可以用梯度下降法(Gradient Descent)去尋找適合的w,找到w後就可以進行預測了!
In [28]:
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
In [29]:
clf = LogisticRegression()
clf.fit(X, y)
Out[29]:
In [30]:
score = cross_val_score(clf, X, y, cv=5)
print (score.mean())
算完之後你會發現,準確率只有50趴,那這樣跟我隨便猜有什麼不一樣,
那先來調整一下參數看準確率會不會提高好了,因此調整一下C
In [31]:
clf = LogisticRegression(C=0.001)
clf.fit(X, y)
Out[31]:
In [32]:
score = cross_val_score(clf, X, y, cv=5)
print (score.mean())
準確率大幅上升,原因是因為 參數C在Scikit-learn裡LogisticRegression定義為正規化參數(regularization parameter)λ的倒數,
而正規化(regularization)是用來防止高度適合(overfitting)
也就是說C變得很小,那λ就會變很大,正規化的強度就會變強防止overfitting