#!/usr/bin/env python # coding: utf-8 # # **로지스틱 회귀를 이용한 Click Through Rate** # 1. 데이터 전처리 및 One-Hot Encoing # 1. **Logistic 회귀** 동작의 원리 # 1. **Gradient descent** 기법, **Statistic Gradient descent** 기법 # 1. **Logistic 회기** 분류기 학습 및 예측모델 # 1. **L1, L2 정규화**를 이용한 Logistic 회귀 # 1. On - Line Learning # 1. **Random Forest** 를 이용한 **feacture selection** #
# # # **1 One - Hot Encoding** # 1. **범주형 feacture** 를 **이진형 수치 feacture** 로 변환 # 1. **K개의 값**을 갖는 **범주형** feacture를 **1~k 의** feacture로 매핑시킨다 # 1. 변환된 범주형 데이터를 **원본으로** 되돌린다 # ## **01 One - Hot Encoding 임베딩 데이터 만들기** # 1. **범주형 feacture** 를 **이진형 수치 feacture** 로 변환 # 1. **K개의 값**을 갖는 **범주형** feacture를 **1~k 의** feacture로 매핑시킨다 # 1. 변환된 범주형 데이터를 **원본으로** 되돌린다 # In[1]: # Dict 범주형 데이터를 One-Hot-encoding으로 변환 from sklearn.feature_extraction import DictVectorizer dict_one_hot_encoder = DictVectorizer(sparse=False) X_dict = [{'interest': 'tech', 'occupation': 'professional'}, {'interest': 'fashion', 'occupation': 'student'}, {'interest': 'fashion', 'occupation': 'professional'}, {'interest': 'sports', 'occupation': 'student'}, {'interest': 'tech', 'occupation': 'student'}, {'interest': 'tech', 'occupation': 'retired'}, {'interest': 'sports', 'occupation': 'professional'}] X_encoded = dict_one_hot_encoder.fit_transform(X_dict) X_encoded # In[2]: # 범주형 Dataset Index 매핑내용 살펴보기 from pprint import pprint pprint(dict_one_hot_encoder.vocabulary_) # ## **02 Converting Data by Using Map Data** # 위에서 학습한 **dict_one_hot_encoder** 를 활용하여 데이터를 컨버팅/ 복원 # In[3]: # 위에서 매팽한 table 을 사용하여 새로운 데이터 인코딩 new_dict = [{'interest': 'sports', 'occupation': 'retired'}] new_encoded = dict_one_hot_encoder.transform(new_dict) print(new_encoded) # In[4]: # new_encoded 인코딩 데이터를 원본형태로 되돌린다 print(dict_one_hot_encoder.inverse_transform(new_encoded)) # ## **03 Learning New Map Data** # 1. **new_encoded :** 새로운 매핑 데이터 추가하면, 결과적으로 **무시된다** # 1. 두개의 **dict** 데이터 중 **없는건 제외하고 나머지만 Converting** 된다 # In[5]: # 1개의 인덱스에 포함된 2개의 Dict 중, 1개만 converting 된다 new_dict = [{'interest': 'unknown_interest', 'occupation': 'retired'}, {'interest': 'tech', 'occupation': 'unseen_occupation'}] new_encoded = dict_one_hot_encoder.transform(new_dict) print(new_encoded) # ## **04 LabelEncoder 를 활용한 One-Hot-Encoding** # 1. **X_int** : One Hot 의 **인덱스값을** 출력한다 # 1. 보다 간결하고 식별력이 높다 # In[6]: import numpy as np X_str = np.array([['tech', 'professional'], ['fashion', 'student'], ['fashion', 'professional'], ['sports', 'student'], ['tech', 'student'], ['tech', 'retired'], ['sports', 'professional']]) from sklearn.preprocessing import LabelEncoder, OneHotEncoder label_encoder = LabelEncoder() X_int = label_encoder.fit_transform(X_str.ravel()).reshape(*X_str.shape) print(X_int) # In[7]: # X_int 를 X_encoded 로 변환 one_hot_encoder = OneHotEncoder() X_encoded = one_hot_encoder.fit_transform(X_int).toarray() print(X_encoded) # In[8]: # Mapping 입력되지 않은 값들은 위와 동일하게 무시된다 new_str = np.array([['unknown_interest', 'retired'], ['tech', 'unseen_occupation'], ['unknown_interest', 'unseen_occupation']]) def string_to_dict(columns, data_str): data_dict = [] for sample_str in data_str: data_dict.append({column : value for column, value in zip(columns, sample_str)}) return data_dict columns = ['interest', 'occupation'] new_encoded = dict_one_hot_encoder.transform(string_to_dict(columns, new_str)) print(new_encoded) #
# # # **2 로지스틱 회귀 분류기** # 1. **실수값 데이터는 0~1 사이의 값으로** 변환한다 # 1. $y(z) = \frac{1}{1+exp(-z)}$ 대용량 데이터에 **확장성이 좋은** 알고리즘이다 # ## **01 로지스틱 회귀의 동작원리** # 로지스틱 회귀는 나이브 베이즈 분류기처럼 **확률 기반 분류기이다** # In[9]: # 로지스틱 회귀 함수를 정의한다 import numpy as np def sigmoid(input): return 1.0 / (1 + np.exp(-input)) # In[10]: get_ipython().run_line_magic('matplotlib', 'inline') # -8~8 사이의 값으로 로지스틱 회귀모델을 구현 import matplotlib.pyplot as plt plt.figure(figsize=(5,3)) z = np.linspace(-8, 8, 1000) y = sigmoid(z) plt.plot(z, y) plt.axhline(y=0, ls='dotted', color='k') plt.axhline(y=0.5, ls='dotted', color='k') plt.axhline(y=1, ls='dotted', color='k') plt.yticks([0.0, 0.25, 0.5, 0.75, 1.0]) plt.xlabel('z'); plt.ylabel('y(z)'); plt.show() # ## **02 MSE를 최소로 하는 로지스틱 회귀** # 비용함수를 최소로(실질적으로는 **MSE기반의 비용함수를** 최소로)하는 값들을 예측한다 # In[11]: # plot sample cost vs y_hat (prediction), for y (truth) = 1 y_hat = np.linspace(0, 1, 1000) cost = -np.log(y_hat) plt.figure(figsize=(4,3)) plt.plot(y_hat, cost) plt.xlabel('Prediction'); plt.ylabel('Cost') plt.xlim(0, 1); plt.ylim(0, 7); plt.show() # In[12]: # plot sample cost vs y_hat (prediction), for y (truth) = 0 y_hat = np.linspace(0, 1, 1000) cost = -np.log(1 - y_hat) plt.figure(figsize=(4,3)) plt.plot(y_hat, cost) plt.xlabel('Prediction'); plt.ylabel('Cost') plt.xlim(0, 1); plt.ylim(0, 7); plt.show() #
# # # **3 그레디언트 하강을 활용한 로지스틱 회귀** # 1. 단볼록이 아닌, 비볼록 형태의 데이터에 대한 로지스틱 회귀 최적값을 예측한다 # ## **01 그레디언트 하강기법의 로지스틱 함수 정의** # 로지스틱 회귀는 나이브 베이즈 분류기처럼 **확률 기반 분류기이다** # In[13]: # 현재의 가중치 값을 사용하여 예측값을 계산하는 함수 def compute_prediction(X, weights): z = np.dot(X, weights) predictions = sigmoid(z) return predictions # Gradient 하강 기법을 단계저긍로 정의하여 가중치를 업데이트 한다 def update_weights_gd(X_train, y_train, weights, learning_rate): predictions = compute_prediction(X_train, weights) weights_delta = np.dot(X_train.T, y_train - predictions) m = y_train.shape[0] weights += learning_rate / float(m) * weights_delta return weights # updated weights(numpy.ndarray) # In[14]: # 비용을 계산하는 함수를 계산한다 def compute_cost(X, y, weights): predictions = compute_prediction(X, weights) cost = np.mean(-y * np.log(predictions) - (1-y) * np.log(1-predictions)) return cost # float # 로지스틱 회귀 모델을 학습한다 def train_logistic_regression(X_train, y_train, max_iter, learning_rate, fit_intercept=False): if fit_intercept: intercept = np.ones((X_train.shape[0], 1)) # .hstack() 행의 수가 같은 두 개 이상의 배열을 옆으로 연결 X_train_np = np.hstack((intercept, X_train)) weights = np.zeros(X_train_np.shape[1]) for iteration in range(max_iter): weights = update_weights_gd(X_train_np, y_train, weights, learning_rate) if iteration % 1000 == 0: # 1000번 학습을 반복한다 print("{:,}th Logistic Cost : {:.5f}".format( iteration, compute_cost(X_train_np, y_train, weights))) return weights # In[15]: # 학습모델을 이용하여 새로운 데이터의 결과를 예측하는 함수 def predict(X, weights): if X.shape[1] == weights.shape[0] - 1: intercept = np.ones((X.shape[0], 1)) X = np.hstack((intercept, X)) return compute_prediction(X, weights) # ## **02 예제 데이터를 활용하여 모델을 학습한다** # 1. **절편값이 포함된** 가중치 함수를 기반으로 학습한다 # 1. **학습률은 0.1**, 로지스틱 회귀 모델을 **1,000번 반복하여** 학습한다 # In[16]: # iterator를 반복할수록 학습의 Cost 값이 줄어듬을 알 수 있다 X_train = np.array([[6, 7],[2, 4],[3, 6],[4, 7],[1, 6], [5, 2],[2, 0],[6, 3],[4, 1],[7, 2]]) y_train = np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]) weights = train_logistic_regression(X_train, y_train, max_iter = 10000, learning_rate = 0.1, fit_intercept = True) # In[17]: X_test = np.array([[6, 1],[1, 3],[3, 1],[4, 5]]) predictions = predict(X_test, weights) predictions # In[18]: # 분류 판단을 위한 임계치로 0.5를 설정하여 결과를 출력한다 # Train 데이터로 학습한 모델이, 새로운 데이터에 대해서도 잘 적용됨을 볼 수 있다 plt.scatter(X_train[:,0], X_train[:,1], marker = 'o', c = ['b'] * 5 + ['k'] * 5) colours = ['k' if prediction >= 0.5 else 'b' for prediction in predictions] plt.scatter(X_test[:,0], X_test[:,1], marker = '*', c = colours) plt.xlabel('x1'); plt.ylabel('x2'); plt.show() #
# # # **4 그레디언트 하강과 로지스틱 화귀를 이용한 CTR 예측** # Click Through Rate # ## **01 알고리즘에 1K개의 데이터로 학습** # 데이터 Set **앞 1,000개로** 학습, **뒤의 1,000개로** 테스트 한다 # In[19]: import csv def read_ad_click_data(n, offset=0): X_dict, y = [], [] with open('./data/train.csv', 'r') as csvfile: reader = csv.DictReader(csvfile) for i in range(offset): next(reader) i = 0 for row in reader: i += 1 y.append(int(row['click'])) del row['click'], row['id'], row['hour'], row['device_id'], row['device_ip'] X_dict.append(dict(row)) if i >= n: break return X_dict, y n = 1000 X_dict_train, y_train = read_ad_click_data(n) print(X_dict_train[0]) print(X_dict_train[1]) # In[20]: # 데이터 학습을 위해 One-Hot-Encoding 객체로 임베딩 from sklearn.feature_extraction import DictVectorizer dict_one_hot_encoder = DictVectorizer(sparse=False) X_train = dict_one_hot_encoder.fit_transform(X_dict_train) X_dict_test, y_test_1k = read_ad_click_data(n, n) X_test = dict_one_hot_encoder.transform(X_dict_test) X_train_1k = X_train y_train_1k = np.array(y_train) import timeit start_time = timeit.default_timer() weights = train_logistic_regression(X_train_1k, y_train_1k, max_iter=10000, learning_rate=0.01, fit_intercept=True) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) # In[21]: # 위에서 학습한 모델의 정확도 측정 X_test_1k = X_test predictions = predict(X_test_1k, weights) from sklearn.metrics import roc_auc_score print('The ROC AUC on testing set is: {0:.3f}'.format(roc_auc_score(y_test_1k, predictions))) # ## **02 SGD 그래디언트 하강기법을 사용** # 1. **update_weights_sgd()** 함수를 사용 # 1. **SGD 기법으로** 데이터 Set **앞 1,000개로** 학습, **뒤의 1,000개로** 테스트 한다 # In[22]: # SGD를 이용한 로지스틱 회귀 알고리즘에 맞게 수정 def update_weights_sgd(X_train, y_train, weights, learning_rate): for X_each, y_each in zip(X_train, y_train): prediction = compute_prediction(X_each, weights) weights_delta = X_each.T * (y_each - prediction) weights += learning_rate * weights_delta return weights # In[23]: # update_weights_sgd() 로 로지스틱 회귀 알고리즘을 적용 def train_logistic_regression(X_train, y_train, max_iter, learning_rate, fit_intercept=False): if fit_intercept: intercept = np.ones((X_train.shape[0], 1)) X_train = np.hstack((intercept, X_train)) weights = np.zeros(X_train.shape[1]) for iteration in range(max_iter): weights = update_weights_sgd(X_train, y_train, weights, learning_rate) # Check the cost for every 2 (for example) iterations if iteration % 2 == 0: print("{:,}th SGD Logistic : {:.5f}".format( iteration, compute_cost(X_train, y_train, weights))) return weights # In[24]: # 1K 샘플 데이터로 SGD model start_time = timeit.default_timer() weights = train_logistic_regression(X_train_1k, y_train_1k, max_iter=5, learning_rate=0.01, fit_intercept=True) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) # In[25]: predictions = predict(X_test_1k, weights) print('The ROC AUC on testing set is: {0:.3f}'.format(roc_auc_score(y_test_1k, predictions))) # ## **03 SGD 알고리즘에 10K개의 데이터로 학습** # 1. 데이터 Set **앞 10,000개로** 학습, **뒤의 10,000개로** 테스트 한다 # 1. 훨씬 속도도 빠르고 모델의 결과도 좋게 출력된다 # In[26]: n = 10000 X_dict_train, y_train = read_ad_click_data(n) dict_one_hot_encoder = DictVectorizer(sparse=False) X_train = dict_one_hot_encoder.fit_transform(X_dict_train) X_train_10k = X_train y_train_10k = np.array(y_train) # Train the SGD model based on 100000 samples start_time = timeit.default_timer() weights = train_logistic_regression(X_train_10k, y_train_10k, max_iter=5, learning_rate=0.01, fit_intercept=True) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) # In[27]: X_dict_test, y_test_10k = read_ad_click_data(10000, 10000) X_test_10k = dict_one_hot_encoder.transform(X_dict_test) predictions = predict(X_test_10k, weights) print('The ROC AUC on testing set is: {0:.3f}'.format(roc_auc_score(y_test_10k, predictions))) #
# # # **5 Sklearn 을 활용한 SGD 알고리즘으로 CTR 예측** # scikit-learn 모듈의 활용 # In[28]: # Use scikit-learn package from sklearn.linear_model import SGDClassifier sgd_lr = SGDClassifier(loss='log', penalty=None, fit_intercept=True, max_iter=5, learning_rate='constant', eta0=0.01) sgd_lr.fit(X_train_10k, y_train_10k) predictions = sgd_lr.predict_proba(X_test_10k)[:, 1] print('The ROC AUC on testing set is: {0:.3f}'.format(roc_auc_score(y_test_10k, predictions))) #
# # # **6 정규화 기법을 이용한 SGD** # **L1 정규화 기법을** 활용한 로지스틱 회귀모델 **Feature Selection** # In[29]: l1_feature_selector = SGDClassifier(loss = 'log', penalty = 'l1', alpha = 0.0001, fit_intercept = True, max_iter = 5, learning_rate = 'constant', eta0 = 0.01) l1_feature_selector.fit(X_train_10k, y_train_10k) # In[30]: # 중요 Feature 정규화 cf) transform 은 작동하지 않는다 (Coding Error) # X_train_10k_selected = l1_feature_selector.transform(X_train_10k) print("원본 데이터 Set : ", X_train_10k.shape) print("Ramdom Forest 로 특징 feature 선별 : ", X_train_10k_selected.shape) # In[31]: # 하위 10 개의 weights 그리고 the corresponding 10 least important features print(np.sort(l1_feature_selector.coef_)[0][:10]) print(np.argsort(l1_feature_selector.coef_)[0][:10]) # In[32]: # 상위 10 개의 weights and the corresponding 10 most important features print(np.sort(l1_feature_selector.coef_)[0][-10:]) print(np.argsort(l1_feature_selector.coef_)[0][-10:]) #
# # # **7 온라인 러닝 대규모 데이터세트 학습** # 실시간 데이터는 **청크 데이터**로 **작은 크기로 전처리 작업을** 수행한다 # In[33]: # The number of iterations is set to 1 if using partial_fit. sgd_lr = SGDClassifier(loss='log', penalty=None, fit_intercept=True, max_iter=1, learning_rate='constant', eta0=0.01) import timeit start_time = timeit.default_timer() # there are 40428968 labelled samples, use the first ten 100k samples for training, and the next 100k for testing for i in range(20): X_dict_train, y_train_every_100k = read_ad_click_data(1000, i * 1000) X_train_every_100k = dict_one_hot_encoder.transform(X_dict_train) sgd_lr.partial_fit(X_train_every_100k, y_train_every_100k, classes=[0, 1]) print("--- %0.3fs seconds ---" % (timeit.default_timer() - start_time)) # In[34]: X_dict_test, y_test_next10k = read_ad_click_data(1000, (i + 1) * 1000) X_test_next10k = dict_one_hot_encoder.transform(X_dict_test) predictions = sgd_lr.predict_proba(X_test_next10k)[:, 1] print('The ROC AUC on testing set is: {0:.3f}'.format(roc_auc_score(y_test_next10k, predictions))) #
# # # **8 다중 클래스의 분류처리** # 전체 **20개의 카테고리로** 분류된 텍스트를 **SGD 를 활용한** 모델링 # In[35]: # 뉴스그룹 데이터를 호출하기 & 텍스트 전처리를 작업한다 from nltk.corpus import names from nltk.stem import WordNetLemmatizer all_names = set(names.words()) lemmatizer = WordNetLemmatizer() def letters_only(astr): for c in astr: if not c.isalpha(): return False return True def clean_text(docs): cleaned_docs = [] for doc in docs: cleaned_docs.append(' '.join([lemmatizer.lemmatize(word.lower()) for word in doc.split() if letters_only(word) and word not in all_names])) return cleaned_docs # In[36]: # 뉴스그룹 데이터를 불러온다 from sklearn.datasets import fetch_20newsgroups data_train = fetch_20newsgroups(subset='train', categories=None, random_state=42) data_test = fetch_20newsgroups(subset='test', categories=None, random_state=42) # 텍스트를 전처리 cleaned_train = clean_text(data_train.data) cleaned_test = clean_text(data_test.data) # 라벨링 처리 label_train = data_train.target label_test = data_test.target # In[37]: # 전처리 작업된 텍스트를 Tf-IDF로 변환 from sklearn.feature_extraction.text import TfidfVectorizer tfidf_vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5, stop_words='english', max_features=40000) term_docs_train = tfidf_vectorizer.fit_transform(cleaned_train) term_docs_test = tfidf_vectorizer.transform(cleaned_test) # grid search 검색을 적용 from sklearn.model_selection import GridSearchCV parameters = {'penalty': ['l2', None], 'alpha' : [1e-07, 1e-06, 1e-05, 1e-04], 'eta0' : [0.01, 0.1, 1, 10]} # In[38]: # SGD 분류기를 활용하여 예측모델을 생성한다 sgd_lr = SGDClassifier(loss='log', learning_rate='constant', eta0=0.01, fit_intercept=True, max_iter=10) grid_search = GridSearchCV(sgd_lr, parameters, n_jobs=-1, cv=3) grid_search.fit(term_docs_train, label_train) print(grid_search.best_params_) # In[41]: accuracy = sgd_lr_best.score(term_docs_test, label_test) print('The accuracy on testing set is: {0:.1f}%'.format(accuracy*100)) grid_search.best_estimator_ #
# # # **9 Feature Selection 과 Random Forest 비교** # 1. **feature_importance_** : feature의 중요도를 출력 # In[42]: # Random Foreset 로 중요도 높은 500개의 데이터를 추출한다 from sklearn.ensemble import RandomForestClassifier random_forest = RandomForestClassifier(n_estimators = 100, criterion = 'gini', min_samples_split = 30, n_jobs = -1) random_forest.fit(X_train_10k, y_train_10k) top500_feature = np.argsort(random_forest.feature_importances_)[-500:] X_train_10k_selected = X_train_10k[:, top500_feature] # In[43]: # 상관성 중요도 하위 10 위 가중치 출력 print(np.sort(random_forest.feature_importances_)[:10]) print(np.argsort(random_forest.feature_importances_)[:10]) # In[44]: # 상관성 중요도 상위 10 위 가중치 출력 (중요도 클수록 나중에 출력) print(np.sort(random_forest.feature_importances_)[-10:]) print(np.argsort(random_forest.feature_importances_)[-10:]) # In[45]: # 393번째 학습모델이 상위 print(dict_one_hot_encoder.feature_names_[393]) # In[46]: # 상위 500개의 feature를 선택 출력한다 top500_feature = np.argsort(random_forest.feature_importances_)[-500:] X_train_10k_selected = X_train_10k[:, top500_feature] print(X_train_10k_selected.shape)