上一篇寫了推薦系統最古老的的一種算法叫協同過濾,古老並非很差用,其實仍是很好用的一種算法,隨着時代的進步,出現了神經網絡和因子分解等更優秀的算法解決不一樣的問題。 這裏主要說一下邏輯迴歸,邏輯迴歸主要用於打分的預估。我這裏沒有打分的數據因此用性別代替。 這裏的例子就是用歌曲列表預判用戶性別。python
邏輯迴歸的資料比較多,我比較推薦你們看刷一下bilibili上李宏毅老師的視頻,這裏我只說一些須要注意的點。算法
邏輯迴歸能夠理解爲一種單層神經網絡,網絡結構如圖: 網絡
邏輯迴歸通常選sigmoid或者softmaxdom
別問我啥是sigmoid啥是softmax,問就是百度。elasticsearch
損失函數邏輯迴歸經常使用的有三種(其實有不少不止三種,本身查API嘍):函數
梯度降低方式有不少,我這裏選擇隨機梯度降低,sgd其實我以爲adam更合適,看你們心情了。至於爲啥工具
此次的數據是1萬條KTV唱歌數據,別問我數據哪來的。問就是別人給的。測試
X是用戶唱歌數據的one-hot優化
Y是用戶的性別one-hotui
下面是真正的技術
下面代碼都幹了些啥呢,主要是兩個matrix。
一個是用戶唱歌的onehot->song_hot_matrix。
一個是用戶性別的onehot->decades_hot_matrix。 代碼不重要,主要看字。
import elasticsearch import elasticsearch.helpers import re import numpy as np import operator import datetime es_client = elasticsearch.Elasticsearch(hosts=["localhost:9200"]) def trim_song_name(song_name): """ 處理歌名,過濾掉無用內容和空白 """ song_name = song_name.strip() song_name = re.sub("【.*?】", "", song_name) song_name = re.sub("(.*?)", "", song_name) return song_name def trim_address_name(address_name): """ 處理地址 """ return str(address_name).strip() def get_data(size=0): """ 獲取uid=>做品名list的字典 """ cur_size=0 song_dic = {} user_address_dic = {} user_decades_dic = {} search_result = elasticsearch.helpers.scan( es_client, index="ktv_user_info", doc_type="ktv_works", scroll="10m", query={ "query":{ "range": { "birthday": { "gt": 63072662400 } } } } ) for hit_item in search_result: cur_size += 1 if size>0 and cur_size>size: break user_info = hit_item["_source"] item = get_work_info(hit_item["_id"]) if item is None: continue work_list = item['item_list'] if len(work_list)<2: continue if user_info['gender']==0: continue if user_info['gender']==1: user_info['gender']="男" if user_info['gender']==2: user_info['gender']="女" song_dic[item['uid']] = [trim_song_name(item['songname']) for item in work_list] user_decades_dic[item['uid']] = user_info['gender'] user_address_dic[item['uid']] = trim_address_name(user_info['address']) return (song_dic, user_address_dic, user_decades_dic) def get_user_info(uid): """ 獲取用戶信息 """ ret = es_client.get( index="ktv_user_info", doc_type="ktv_works", id=uid ) return ret['_source'] def get_work_info(uid): """ 獲取用戶信息 """ try: ret = es_client.get( index="ktv_works", doc_type="ktv_works", id=uid ) return ret['_source'] except Exception as ex: return None def get_uniq_song_sort_list(song_dict): """ 合併重複歌曲並按歌曲名排序 """ return sorted(list(set(np.concatenate(list(song_dict.values())).tolist()))) from sklearn import preprocessing %run label_encoder.ipynb user_count = 4000 song_count = 0 # 得到用戶唱歌數據 song_dict, user_address_dict, user_decades_dict = get_data(user_count) # 歌曲字典 song_label_encoder = LabelEncoder() song_label_encoder.fit_dict(song_dict, "", True) song_hot_matrix = song_label_encoder.encode_hot_dict(song_dict, True) user_decades_encoder = LabelEncoder() user_decades_encoder.fit_dict(user_decades_dict) decades_hot_matrix = user_decades_encoder.encode_hot_dict(user_decades_dict, False)
uid | 洗刷刷 | 麻雀 | 你的答案 |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 1 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 0 | 0 |
uid | 男 | 女 |
---|---|---|
0 | 1 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 0 | 1 |
import numpy as np from keras.models import Sequential from keras.layers import Dense, Activation, Embedding,Flatten import matplotlib.pyplot as plt from keras.utils import np_utils from sklearn import datasets from sklearn.model_selection import train_test_split n_class=user_decades_encoder.get_class_count() song_count=song_label_encoder.get_class_count() print(n_class) print(song_count) # 拆分訓練數據和測試數據 train_X,test_X, train_y, test_y = train_test_split(song_hot_matrix, decades_hot_matrix, test_size = 0.2, random_state = 0) train_count = np.shape(train_X)[0] # 構建神經網絡模型 model = Sequential() model.add(Dense(input_dim=8, units=n_class)) model.add(Activation('softmax')) # 選定loss函數和優化器 model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy']) # 訓練過程 print('Training -----------') for step in range(train_count): scores = model.train_on_batch(train_X, train_y) if step % 50 == 0: print("訓練樣本 %d 個, 損失: %f, 準確率: %f" % (step, scores[0], scores[1]*100)) print('finish!')
數據訓練完了用拆分出來的20%數據測試一下:
# 準確率評估 from sklearn.metrics import classification_report scores = model.evaluate(test_X, test_y, verbose=0) print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100)) Y_test = np.argmax(test_y, axis=1) y_pred = model.predict_classes(test_X) print(classification_report(Y_test, y_pred))
輸出:
accuracy: 78.43% precision recall f1-score support 0 0.72 0.90 0.80 220 1 0.88 0.68 0.77 239 accuracy 0.78 459 macro avg 0.80 0.79 0.78 459 weighted avg 0.80 0.78 0.78 459
而後讓小夥伴們一塊兒來玩耍,嗯準確率100%,完美!
def pred(song_list=[]): blong_hot_matrix = song_label_encoder.encode_hot_dict({"bblong":song_list}, True) y_pred = model.predict_classes(blong_hot_matrix) return user_decades_encoder.decode_list(y_pred) # # 男A # print(pred(["一路向北", "暗香", "菊花臺"])) # # 男B # print(pred(["不要說話", "平凡之路", "李白"])) # # 女A # print(pred(["滿足", "被風吹過的夏天", "龍捲風"])) # # 男C # print(pred(["情人","再見","無賴","離人","你的樣子"])) # # 男D # print(pred(["小情歌","我好想你","無與倫比的美麗"])) # # 男E # print(pred(["忐忑","最炫民族風","小蘋果"]))