顯示具有 sklearn 標籤的文章。 顯示所有文章
顯示具有 sklearn 標籤的文章。 顯示所有文章

2020年3月28日 星期六

[Python] 數據分析筆記 - 透過 pandas, scikit-learn 和 xgboost 分析 Kaggle airbnb-recruiting-new-user-bookings 案例

記得 2017 年也曾註冊 Kaggle 帳號,在上面挑個題目試試手氣,當時也有選中 Airbnb 來研究,可惜當年沒堅持下去,太多有趣的事了 XD 今年春天就來複習一下。

當初我是先從史丹佛 Andrew Ng 的課程看的,但大概只看個幾集就沒繼續,再過一陣子後就是台大教授林軒田的機器學習基石,我印象中有看完,因為我還在遲疑要不要接著看另一個進階課程,那時過境遷,沒再用都忘光了!
這些課程看著看著就不小心恍神了,接著自己僅用著一些原理去土砲...殊不知 Pandas 跟 scikit-learn 套件有多好用,當時只粗略用 Pandas 當 csv parser ,剩下的資料轉換、陣列計算還自己刻 numpy 架構去運算。所幸,終於有個更適合我這種懶人的微課程,那就是 Kaggle 的 Faster Data Science Education
我大概只需要從第三章 Intermediate Machine Learning 走完一遍就得到我想要的東西了。接著想找個戰場試試手氣,就又回想起 Kaggle 的 airbnb-recruiting-new-user-bookings 數據
接著用 airbnb-recruiting-new-user-bookings 關鍵字問個 google ,會發現到現在也有非常多人拿他當例子來分析,經典果真歷久不衰!大概不用破百行,就可以組出 airbnb 數據分析達八成的水準:

匯入函式庫:

from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from xgboost import XGBClassifier

import numpy as np
import pandas as pd

import datetime


匯入 csv 檔案:

train_users = pd.read_csv('input/train_users_2.csv')

將 age 內容調整,包含去除輸入錯誤(太大或大小者),例如明顯輸入的是西元年,就順便幫轉一下:

data_checker = train_users.select_dtypes(include=['number']).copy()
data_checker = data_checker[ (data_checker.age > 1000) & (data_checker.age < 2010) ]
data_checker['age'] = 2015 - data_checker['age'] # 推論當年的資料,用 2015 年來相減對方不小心輸錯的出生年來得到年紀

for idx,row in data_checker.iterrows():
        train_users.at[idx,'age'] = row['age']

data_checker = train_users.select_dtypes(include=['number']).copy()
data_checker = data_checker[ (data_checker.age >= 2010) | (data_checker.age >= 100) | (data_checker.age < 13) ]
data_checker['age'] = np.nan
for idx,row in data_checker.iterrows():
        train_users.at[idx,'age'] = row['age']


處理時間欄位,轉成 datetime 型態,並轉成 weekday:

data_checker = train_users.loc[:, 'timestamp_first_active'].copy()
data_checker = pd.to_datetime( (data_checker // 1000000), format='%Y%m%d')
train_users['timestamp_first_active'] = data_checker

str_to_datetime_fields = ['date_account_created', 'date_first_booking']

for field in str_to_datetime_fields:
        train_users[field] = pd.to_datetime(train_users[field])

# to weekday

train_users['first_active_weekday'] = train_users['timestamp_first_active'].dt.dayofweek
for field in str_to_datetime_fields:
        train_users[field+'_weekday'] = train_users[field].dt.dayofweek

# remove datetime fields

train_users.drop(str_to_datetime_fields, axis=1, inplace=True)
train_users.drop(['timestamp_first_active'], axis=1, inplace=True)


處理 label 資料,主要轉成 one-hot encoding,並且去除一些數值,統一轉成 NaN:

categorical_features = [
        'affiliate_channel',
        'affiliate_provider',
        #'country_destination',
        'first_affiliate_tracked',
        'first_browser',
        'first_device_type',
        'gender',
        'language',
        'signup_app',
        'signup_method'
]
for categorical_feature in categorical_features:
        train_users[categorical_feature].replace('-unknown-', np.nan, inplace=True)
        train_users[categorical_feature].replace('NaN', np.nan, inplace=True)
        train_users[categorical_feature] = train_users[categorical_feature].astype('category')

# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html
# Convert categorical variable into dummy/indicator variables.
train_users = pd.get_dummies(train_users, columns=categorical_features)


開始順練模型,建議先透過 sample 跑小量

# X = train_users.copy()
X = train_users.sample(n=3000,random_state=0).copy()
y = X['country_destination'].copy()
X = X.drop(['country_destination'], axis=1)

X_train, X_valid, y_train, y_valid = train_test_split(X, y)


print("Start to train...")

job_start = datetime.datetime.now()

my_model = XGBClassifier()
my_model.fit(X_train, y_train)

print("training done, time cost: ", (datetime.datetime.now() - job_start))

job_start = datetime.datetime.now()

predictions = my_model.predict(X_valid)
print("predict done, time cost: ", (datetime.datetime.now() - job_start))

print("score:", accuracy_score(predictions, y_valid))


運行結果:

Start to train...
training done, time cost:  0:00:14.270242
predict done, time cost:  0:00:00.056500
score: 0.8466666666666667


沒想到只需做一些處理,運算玩就有八成準確率了!以上還沒使用 sessions 資料。完整程式碼請參考:github.com/changyy/study-kaggle-airbnb-recruiting-new-user-bookings

2017年9月15日 星期五

[Python] 機器學習筆記 - 使用 準確率/召回率 (Precision-Recall) 評估分析成果

研究所時,算是第一次接觸這名詞,老闆的研究領域是 Search Engines ,用來評估索引成果好不好。最近則打算用在機器學習的成果分析,卻想不起當時老闆用來解釋索引成果的案例,還是容易忘記 XD 網路上打滾一下,發現這篇寫的廣告投放實際案例很好懂,也不容易忘,建議可以逛一下:準確率(Precision)與召回率(Recall)

回到本文,單純紀錄如何用既有函式庫計算:

import numpy as np
from sklearn.metrics import average_precision_score, precision_score, recall_score

# Classification metrics can't handle a mix of binary and continuous targets
#y = np.array([0, 0, 1, 1])
#scores = np.array([0.1, 0.3, 0.2, 0.8])

y = [0, 0, 1, 1]
scores = [0, 1, 1, 1]

#print(precision_score(y, scores, average='macro'))
#print(recall_score(y, scores, average='macro'))
#print(average_precision_score(y, scores))
#import sys
#sys.exit(0)

print('precision: %0.2f, recall: %0.2f, score: %0.2f' % (
        precision_score(y, scores, average='macro'),
        recall_score(y, scores, average='macro'),
        average_precision_score(y, scores)
))


成果:

precision: 0.83, recall: 0.75, score: 0.67

需要更詳細的範例,請參考:http://scikit-learn.org/stable/auto_examples/model_selection/plot_precision_recall.html

[Python] 機器學習筆記 - 使用 ROC 曲線 (receiver operating characteristic curve) 評估分析成果

roc curve

最近回想起兩年前走跳過的一場黑客松,當年的題目恰好是一個屬性的分類,就是一篇文章屬性給你,請告訴我它是不是 spam!所幸網路上還可以看到其他人的作品,逛了一下也順便研究別人的報告怎麼寫,其中有一組使用了 ROC 曲線來回報自己的分析成果,就來多多認識一下。

而 ROC 曲線是什麼?其實在 WIKI 或是 scikit-learn 文件(也引用WIKI資料)有很明確地解釋:
簡單的說,當畫出此圖後,若一開始就達左上角是最完美的,若一開始分析結果是斜線上方是好的,反之下方是差的。接下來,則是會去計算曲線下方的面積,產生一個介於 0~1 的數值,只要等於 0.5 就是跟隨機猜測一樣,代表此分析模型沒有預測價值;若大於 0.5 代表猜測是正向的,而小於 0.5 代表猜測的方向恰好相反;而 1 或 0 代表全部辨識正確或全部辨識錯誤。

因此,只需設法把模型預測結果畫一下 ROC 曲線,在算出個面積,就收工啦!

範例請參考 http://scikit-learn.org/stable/auto_examples/model_selection/plot_roc.html ,在此只筆記畫圖的部分:

import numpy as np
from sklearn.metrics import roc_curve, auc
y = np.array([0, 0, 1, 1])
scores = np.array([0.1, 0.4, 0.35, 0.8])
fpr, tpr, _ = roc_curve(y, scores)
roc_auc = auc(fpr, tpr)

import matplotlib as mpl
#mpl.use('Agg')
import matplotlib.pyplot as plt

fig = plt.figure()
lw = 2
plt.plot(fpr, tpr, color='darkorange', lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
#fig.savefig('/tmp/roc.png')
plt.show()

2017年8月19日 星期六

[Python] 機器學習筆記 - 透過 sklearn.svm 簡易的數據分析、機器學習萬用框架 @ macOS 10.12, Python36

svm_report

幾年前有幸參加過數據分析的黑客松,但是太耍廢了 XD 當下只用統計硬幹。最近有些閒情想好好認識一下 SVM 了。目前使用它的方式很粗淺 XD 就是把一堆 feature 湊個成 array 餵進去跑,接著就有報表可以看了(當初還人工去計算 precision / recall),回想起來真是青春啊

回過頭來,程式架構如下:

import numpy as np
import pandas as pd # 假設 input 是 csv 格式

# 讀取資料中
raw = pd.read_csv("input.csv")
# 可以得知有多少欄位可以用
print(raw.columns)

# 假設所有屬性都是可以有一對一的對應,全部把他們取代成整數,此為 HASH table 用來轉換而已
LOOK_FIELD = {}

# 假設 raw 有一萬筆資料
USE_DATA_COUNT = 10000 # or raw.size

# 將 raw 資料建置成 numpy array 架構

data_input = None
data_output = None

for index, row in raw.iterrows():

data_per_row = np.empty([])

# 將有興趣的欄位(feature)抽出來使用
for field_name in [
"csv_fieldname1",
"csv_fieldname2",
]:
field_data = np.zeros(1, dtype=np.int)
if field_name not in LOOK_FIELD:
LOOK_FIELD[field_name] = {}
if row[field_name] in LOOK_FIELD[field_name]:
field_data[0] = LOOK_FIELD[field_name][row[field_name]]
else:
field_data[0] = len(LOOK_FIELD[field_name])
LOOK_FIELD[field_name][row[field_name]] = field_data[0]
data_per_row = np.append(data_per_row, field_data.reshape(1, -1))

if data_input is None:
data_input = np.zeros([USE_DATA_COUNT, data_per_row.reshape(1, -1).size], dtype=np.float)
data_input[index] = data_per_row.reshape(1, -1)


result = np.zeros([1], dtype=np.int)

output_field_name = "csv_fieldname3"

# 將 結果 的欄位轉換成數值
if output_field_name not in LOOK_FIELD:
LOOK_FIELD[output_field_name] = {}
if row[output_field_name] in LOOK_FIELD[output_field_name]:
result[0] = LOOK_FIELD[output_field_name][ row[output_field_name] ]
else:
result[0] = len(LOOK_FIELD[output_field_name])
LOOK_FIELD[output_field_name][ row[output_field_name] ] = result[0]

if data_output is None:
data_output = np.zeros([USE_DATA_COUNT, result.reshape(1, ).size], dtype=np.int)
data_output[index] = result.reshape(1, )

# 支援只使用 USE_DATA_COUNT 筆資料
if index >= USE_DATA_COUNT - 1:
break

print(data_input)
print(data_output)
print(data_input.shape)
print(data_output.shape)

from sklearn import svm, metrics

classifier = svm.SVC()

# 使用 1/5 的資料來訓練
number_of_data_to_learn = int(USE_DATA_COUNT / 5) # or int(data_output.size/5)

# start to learn
classifier.fit(data_input[:number_of_data_to_learn], data_output[:number_of_data_to_learn])

# get the result
expected = data_output[number_of_data_to_learn:]
predicted = classifier.predict(data_input[number_of_data_to_learn:])

# get the report
print("Classification report for classifier %s:\n%s\n" % (classifier, metrics.classification_report(expected, predicted)))
print("Confusion matrix:\n%s" % metrics.confusion_matrix(expected, predicted))


透過上述的程式架構,未來就只要把資料轉成 csv ,挑挑 feature (csv_fieldname1, csv_fieldname2) 跟 output (csv_fieldname3) 欄位就可以快速看到成果了 XD 要唬人也可以 3 分鐘就弄出點東西。