1. 程式人生 > >程世東老師TensorFlow實戰——個性化推薦,程式碼學習筆記之資料匯入&資料預處理

程世東老師TensorFlow實戰——個性化推薦,程式碼學習筆記之資料匯入&資料預處理

#執行下面程式碼把資料下載下來
import pandas as pd   
from sklearn.model_selection import train_test_split #資料集劃分訓練集和測試集
import numpy as np
from collections import Counter #counter用於統計字元出現的次數
import tensorflow as tf

import os  #os 模組提供了非常豐富的方法用來處理檔案和目錄
import pickle #提供儲存資料在本地的方法
import re #正則表示式
from tensorflow.python.ops import math_ops
from urllib.request import urlretrieve #將URL表示的網路物件複製到本地檔案
from os.path import isfile, isdir #判斷是否存在檔案file,資料夾dir
from tqdm import tqdm #Tqdm 是一個快速,可擴充套件的Python進度條,可以在 Python 長迴圈中新增一個進度提示資訊,使用者只需要封裝任意的迭代器 tqdm(iterator)
import zipfile #用來做zip格式編碼的壓縮和解壓縮的
import hashlib #用來進行hash 或者md5 加密

def _unzip(save_path, _, database_name, data_path):
    """
    Unzip wrapper with the same interface as _ungzip使用與_ungzip相同的介面解壓縮包裝器
    :param save_path: gzip檔案的路徑
    :param database_name:資料庫的名稱
    :param data_path: 提取路徑
    :param _: HACK - Used to have to same interface as _ungzip 用於與_ungzip具有相同的介面??解壓後的檔案路徑
    """
    print('Extracting {}...'.format(database_name))#.format通過 {} 來代替字串database_name
    with zipfile.ZipFile(save_path) as zf: #ZipFile是zipfile包中的一個類,用來建立和讀取zip檔案
        zf.extractall(data_path) #類函式zipfile.extractall([path[, member[, password]]]) 
                                 #path解壓縮目錄,沒什麼可說的 
                                 # member需要解壓縮的檔名列表 
                                 # password當zip檔案有密碼時需要該選項 

def download_extract(database_name, data_path):
    """
    下載並提取資料庫
    :param database_name: Database name
    data_path 這裡為./表示當前目錄
    save_path 下載後資料的儲存路徑即壓縮檔案的路徑
    extract_path 解壓後的檔案路徑
    """
    DATASET_ML1M = 'ml-1m'

    if database_name == DATASET_ML1M:
        url = 'http://files.grouplens.org/datasets/movielens/ml-1m.zip'
        hash_code = 'c4d9eecfca2ab87c1945afe126590906'
        extract_path = os.path.join(data_path, 'ml-1m')#os.path.join將多個路徑組合後返回,提取資料的路徑
        save_path = os.path.join(data_path, 'ml-1m.zip')#要儲存的路徑
        extract_fn = _unzip 

    if os.path.exists(extract_path):  #指定路徑(檔案或者目錄)是否存在
        print('Found {} Data'.format(database_name))
        return

    if not os.path.exists(data_path):  #指定路徑(檔案或者目錄)不存在,則遞迴建立目錄data_path
        os.makedirs(data_path) 

    if not os.path.exists(save_path): #指定路徑(檔案或者目錄)不存在,則遞迴建立目錄save_path
        with DLProgress(unit='B', unit_scale=True, miniters=1, desc='Downloading {}'.format(database_name)) as pbar:#呼叫類,進度條顯示相關,tqdm相關引數設定
            urlretrieve(url, save_path, pbar.hook) #urlretrieve()方法直接將遠端資料下載到本地 rlretrieve(url, filename=None, reporthook=None, data=None)
                                                    #filename指定了儲存本地路徑
                                                    #reporthook是回撥函式,當連線上伺服器、以及相應的資料塊傳輸完畢時會觸發該回調,可利用回撥函式顯示當前下載進度。


    assert hashlib.md5(open(save_path, 'rb').read()).hexdigest() == hash_code, \
        '{} file is corrupted.  Remove the file and try again.'.format(save_path)
     #assert expression [, arguments]表示斷言測試,如expression異常,則輸出後面字串資訊
     #能指出資料是否被篡改過,就是因為摘要函式是一個單向函式,計算f(data)很容易,但通過digest反推data卻非常困難。而且,對原始資料做一個bit的修改,都會導致計算出的摘要完全不同。
     #摘要演算法應用:使用者儲存使用者名稱密碼,但在資料庫不能以明文儲存,而是用md5,當一個使用者輸入密碼時,進行md5匹配,如果相同則可以登入
     #hashlib提供了常見的摘要演算法,如MD5,SHA1等等。摘要演算法又稱雜湊演算法、雜湊演算法。它通過一個函式,把任意長度的資料轉換為一個長度固定的資料串(通常用16進位制的字串表示)。
     #hexdigest為md5後的結果

    os.makedirs(extract_path)
    try:
        extract_fn(save_path, extract_path, database_name, data_path)#解壓
    except Exception as err:
        shutil.rmtree(extract_path)  # Remove extraction folder if there is an error表示遞迴刪除資料夾下的所有子資料夾和子檔案
        raise err#丟擲異常

    print('Done.')
    # Remove compressed data
#     os.remove(save_path)

class DLProgress(tqdm):
    """
    Handle Progress Bar while Downloading下載時處理進度條
    """
    last_block = 0

    def hook(self, block_num=1, block_size=1, total_size=None):
        """
        該函式在建立網路連線時呼叫一次,之後在每個塊讀取後呼叫一次.
        :param block_num: 到目前為止轉移的塊數
        :param block_size: 塊大小(位元組)
        :param total_size: 檔案的總大小。 對於較舊的FTP伺服器,這可能為-1,該伺服器不響應檢索請求而返回檔案大小。
        """
        self.total = total_size
        self.update((block_num - self.last_block) * block_size)
        self.last_block = block_num
data_dir = './'
download_extract('ml-1m', data_dir)

#------------------------------------------------------------------------------
#實現資料預處理
def load_data():
    """
    Load Dataset from File
    """
    #讀取User資料-------------------------------------------------------------
    users_title = ['UserID', 'Gender', 'Age', 'JobID', 'Zip-code']
    users = pd.read_table('./ml-1m/users.dat', sep='::', header=None, names=users_title, engine = 'python')
                                                #分隔符引數:sep
                                                #是否讀取文字資料的header,headers = None表示使用預設分配的列名,一般用在讀取沒有header的資料檔案
                                                #為文字的資料加上自定義列名: names
                                                #pandas.read_csv()從檔案,URL,檔案型物件中載入帶分隔符的資料。預設分隔符為','
                                                #pandas.read_table()從檔案,URL,檔案型物件中載入帶分隔符的資料。預設分隔符為'\t'
    
    users = users.filter(regex='UserID|Gender|Age|JobID')
                                                #DataFrame.filter(items=None, like=None, regex=None, axis=None)
                                                #這裡使用正則式進行過濾
    users_orig = users.values #dataframe.values以陣列的形式返回DataFrame的元素
    
    #改變User資料中性別和年齡
    gender_map = {'F':0, 'M':1}
    users['Gender'] = users['Gender'].map(gender_map) #map()函式可以用於Series物件或DataFrame物件的一列,接收函式或字典物件作為引數,返回經過函式或字典對映處理後的值。

    age_map = {val:ii for ii,val in enumerate(set(users['Age']))}
                                                #enumerate() 函式用於將一個可遍歷的資料物件(如列表、元組或字串)組合為一個索引序列
                                                #同時列出資料和資料下標,一般用在 for 迴圈當中
                                                #set() 函式建立一個無序不重複元素集
    users['Age'] = users['Age'].map(age_map) #map接收的引數是函式

    #讀取Movie資料集---------------------------------------------------------
    movies_title = ['MovieID', 'Title', 'Genres']
    movies = pd.read_table('./ml-1m/movies.dat', sep='::', header=None, names=movies_title, engine = 'python')
    movies_orig = movies.values
    #將Title中的年份去掉
    pattern = re.compile(r'^(.*)\((\d+)\)$')    #re.compile(strPattern[, flag]):把正則表示式的模式和標識轉化成正則表示式物件。供match()和search()這兩個函式使用
                                                #第二個引數flag是匹配模式,取值可以使用按位或運算子'|'表示同時生效
                                                #r表示後面是一個正則表示式''
                                                #^匹配開頭,$匹配結尾,(.*)中的()表示匹配其中的任意正則表示式,.匹配任何字元,*代表可以重複0次或多次
                                                #\(和\):表示對括號的轉義,匹配文字中真正的括號
                                                #(\d+)表示匹配()內的任意字元,\d表示任何數字,+代表數字重複一次或者多次
                                                
    title_map = {val:pattern.match(val).group(1) for ii,val in enumerate(set(movies['Title']))}
                                                #這裡的ii是索引值,val是真正的列表中Title元素
                                                #pattern.match(val)使用Pattern匹配文字val,獲得匹配結果,無法匹配時將返回None
                                                #group獲得一個或多個分組截獲的字串;指定多個引數時將以元組形式返回,分組是按照()匹配順序進行
                                                #這裡group(1)相當於只返回第一組,分組標號從1開始。不填則為返回全部結果
                                                #這裡即完成了將電影名稱的時間去掉
    movies['Title'] = movies['Title'].map(title_map)#title列的電影名轉化為去掉名稱後的電影名


    #電影Title轉數字字典
    title_set = set() #set() 函式建立一個無序不重複元素集,返回一個可迭代物件
    for val in movies['Title'].str.split():#對於電影名稱按空格分,val為整個電影列表中全部單詞
                                            #注意string.split() 不帶引數時,和 string.split(' ') 是有很大區別的
                                            #不帶引數的不會截取出空格,而帶引數的只按一個空格去切分,多出來的空格會被截取出來
                                            #參見https://code.ziqiangxuetang.com/python/att-string-split.html
        title_set.update(val)#新增新元素到集合當中,即完成出現電影中的新單詞時,存下來
    
    title_set.add('<PAD>')#這裡不是numpy.pad函式,只是一個填充表示,也為<PAD>進行編碼
    title2int = {val:ii for ii, val in enumerate(title_set)}#為全部單詞進行像字典一樣進行標註'描述電影的word:數字'格式,即數字字典

    #將電影Title轉成等長數字列表,長度是15
    title_count = 15
    title_map = {val:[title2int[row] for row in val.split()] for ii,val in enumerate(set(movies['Title']))}
                                #for ii,val in enumerate(set(movies['Title']))得到ii索引值和其對應的不重複的一個電影字串val(去掉月份的)
                                #val.split()得到全部被空格分開的電影名稱字串列表,row遍歷電影集中一個電影的全部單詞
                                #title_map得到的是字典,格式為'一個電影字串:[描述這個電影的全部單詞構成的一個對應的數值列表]'
    for key in title_map:
        for cnt in range(title_count - len(title_map[key])):
            title_map[key].insert(len(title_map[key]) + cnt,title2int['<PAD>'])#insert(index, object) 在指定位置index前插入元素object
                                 #index                    ,object  電影key長度少於15就加填充符  
    
    movies['Title'] = movies['Title'].map(title_map)#title欄位的去掉名稱後的電影名轉化為對應的數字列表
                                            #如電影集中的一行資料如下movieid,title,genre
                                            # array([1,
                                               # list([3001, 5100, 275, 275, 275, 275, 275, 275, 275, 275, 275, 275, 275, 275, 275]),
                                               # list([3, 6, 2, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17])], dtype=object)


    #電影型別轉數字字典
    genres_set = set()  
    for val in movies['Genres'].str.split('|'):#對於一個電影的題材進行字串轉化,並用|分割遍歷
        genres_set.update(val)  #set.update()方法用於修改當前集合,可以新增新的元素或集合到當前集合中,如果新增的元素在集合中已存在,則該元素只會出現一次,重複的會忽略。
                                #將描述不同題材的電影存入set
    genres_set.add('<PAD>') #集合add方法:是把要傳入的元素做為一個整個新增到集合中,為<PAD>進行編碼
    genres2int = {val:ii for ii, val in enumerate(genres_set)}#將型別轉化為'字串:數字'格式,即數字字典,同上面電影名稱,一個word對應一個數字
                                                              #而一個電影由多個word構成

    #將電影型別轉成等長數字列表,長度是18
    genres_map = {val:[genres2int[row] for row in val.split('|')] for ii,val in enumerate(set(movies['Genres']))}

    for key in genres_map:
        for cnt in range(max(genres2int.values()) - len(genres_map[key])):
            genres_map[key].insert(len(genres_map[key]) + cnt,genres2int['<PAD>'])
    
    movies['Genres'] = movies['Genres'].map(genres_map)


    #讀取評分資料集--------------------------------------------------------
    ratings_title = ['UserID','MovieID', 'ratings', 'timestamps']
    ratings = pd.read_table('./ml-1m/ratings.dat', sep='::', header=None, names=ratings_title, engine = 'python')
    ratings = ratings.filter(regex='UserID|MovieID|ratings')

    #合併三個表
    data = pd.merge(pd.merge(ratings, users), movies)#通過一個或多個鍵將兩個資料集的行連線起來,類似於 SQL 中的 JOIN
                                                    #合併左dataframe和右datafram,預設為取交集,取交集作為索引鍵

    
    #將資料分成X和y兩張表
    target_fields = ['ratings']
    features_pd, targets_pd = data.drop(target_fields, axis=1), data[target_fields]
                    #features_pd只剩userid和rating作為x表;targets_pd只有rating作為y
                    #刪除表中的某一行或者某一列使用drop,不改變原有的df中的資料,而是返回另一個dataframe來存放刪除後的資料。
    
    features = features_pd.values
    targets_values = targets_pd.values
    
    return title_count, title_set, genres2int, features, targets_values, ratings, users, movies, data, movies_orig, users_orig
    #title_count電影名長度15
    #title_set {索引:去掉年份且不重複的電影名}
    #genres2int {題材字串列表:數字}
    #features 使用者ID+評分,輸入x
    #targets_values 評分,學習目標y
    #返回處理後的ratings,users,movies表,pandas物件
    #返回三表的合併表data
    #moives表中的原始資料值:movies_orig
    #users表中的原始資料值:users_orig
#---------------------------------------------------------------------------
#載入資料並儲存到本地
title_count, title_set, genres2int, features, targets_values, ratings, users, movies, data, movies_orig, users_orig = load_data()
pickle.dump((title_count, title_set, genres2int, 
                features, targets_values, ratings, 
                users, movies, data, movies_orig, users_orig), open('preprocess.p', 'wb'))
                #pickle.dump(obj, file[, protocol])序列化物件,並將結果資料流寫入到檔案物件中
                #引數protocol是序列化模式,預設值為0,表示以文字的形式序列化
                #protocol的值還可以是1或2,表示以二進位制的形式序列化。
#從本地讀取資料
title_count, title_set, genres2int, features, targets_values, ratings, users, movies, data, movies_orig, users_orig = pickle.load(open('preprocess.p', mode='rb'))
    

相關推薦

老師TensorFlow實戰——個性化推薦程式碼學習筆記資料匯入&資料處理(上)

程式碼來自於知乎:https://zhuanlan.zhihu.com/p/32078473 /程式碼地址https://github.com/chengstone/movie_recommender/blob/master/movie_recommender.ipynb 下一篇有一些資料的

老師TensorFlow實戰——個性化推薦程式碼學習筆記資料匯入&資料處理(下)

這篇主要是進行程式碼中的一些數值視覺化,幫助理解 程式碼來自於知乎:https://zhuanlan.zhihu.com/p/32078473 /程式碼地址https://github.com/chengstone/movie_recommender/blob/master/movie_re

老師TensorFlow實戰——個性化推薦程式碼學習筆記推薦過程

個性化推薦第三部分:推薦過程(完結撒花) 程式碼來自於知乎:https://zhuanlan.zhihu.com/p/32078473 /程式碼地址https://github.com/chengstone/movie_recommender/blob/master/movie_recomm

老師TensorFlow實戰——個性化推薦程式碼學習筆記②模型訓練與測試

個性化推薦第二部分:模型訓練 程式碼來自於知乎:https://zhuanlan.zhihu.com/p/32078473 /程式碼地址https://github.com/chengstone/movie_recommender/blob/master/movie_recommender.

老師TensorFlow實戰——個性化推薦程式碼學習筆記資料匯入&資料處理

#執行下面程式碼把資料下載下來 import pandas as pd from sklearn.model_selection import train_test_split #資料集劃分訓練集和測試集 import numpy as np from coll

學習筆記T-SQL插入數據INSERT語法和數據庫編實戰技巧[圖]

INSERT 數據庫 IT 編程 學習 一直以來就有寫博客的習慣,記錄一下學習心得、生活點滴等等,感覺也蠻好的,去年開始萌生了建立一個讀書和文學博客的想法,於是就開始實施了,中途也遇到不少難題,不過還好,都逐漸解決了,雖然我在技術方面有欠缺,但好在有同學們幫忙。 今天想和大家分享的學習筆記

千人千面、個性化推薦解讀數據賦能商家背後的AI技術

div 並且 聊天 接收 線上 體驗 超越 金礦 docke 12月6~7日,由阿裏巴巴集團、阿裏巴巴技術發展部、阿裏雲雲棲社區聯合主辦,以“2016 雙 11 技術創新”為主題的阿裏巴巴技術論壇,來自商家事業部的技術總監魏虎給大家分享了數據賦能商家背後的AI技術。首先對大

乾貨 | 個性化推薦系統五大研究熱點深度學習(一)

【編者按】在這個科技高速發展、資訊爆炸的時代,毫不誇張地說,推薦系統已經完全融入了我們的生活。我們去哪一家餐館、買哪一件衣服、瀏覽哪一類資訊、觀看哪一種視訊,很大程度上都取決於背後的推薦系統。 在本文中,微軟亞洲研究院社會計算組的研究員們從深度學習、知識圖譜、強化學習、使用者畫像、可解釋性推薦等五個方面,展望

乾貨|個性化推薦系統五大研究熱點可解釋推薦(五)

【編者按】微軟亞洲研究院社會計算組的研究員們從深度學習、知識圖譜、強化學習、使用者畫像、可解釋性推薦等五個方面,展望了未來推薦系統發展的方向。 在前幾篇文章中,我們分別介紹了深度學習、知識圖譜、強化學習、使用者畫像在推薦系統中的應用以及未來可能的研究方向。在今天的最後一篇文章中,我們將介紹推薦系

深度學習TensorFlow實戰(一)深度學習基本概述

傳統的機器學習演算法例如SVM、Boosting、最大熵、LR都屬於淺層的機器學習模型,這些模型只有一層隱藏節點,或者沒有隱藏節點(LR),其侷限在於有限樣本和計算單單元對複雜函式的表示能力有限,泛化能力的侷限性也較大。深度學習可以通過學習深層非線性網路結構,實

推薦算法學習筆記

sent ges 智慧 mms 容易 轉換 ext 集合 view 推薦算法舉個簡單的例子,比如有個用戶進來看了一堆內容,我們把他看的所有的歷史行為,嵌入到推薦引擎當中去。這個推薦引擎就會生成個性化的頻道,下次這個用戶再登錄,或者都不用下一次,過幾分鐘之後,他看到的內容就會

[C#學習筆記異步編模式2]BeginInvoke和EndInvoke方法 (轉載)

cti otf 函數返回 編程模式 catch 數值 gin 單線程 blog 為什麽要進行異步回調?眾所周知,普通方法運行,是單線程的,如果中途有大型操作(如:讀取大文件,大批量操作數據庫,網絡傳輸等),都會導致方法阻塞,表現在界面上就是,程序卡或者死掉,界面元素不動了,

《SAS編與數據挖掘商業案例》學習筆記十二

style rename today 降序排序 cat list append span nod 本次重點在:sas數據集管理 主要包含:包含數據集縱向拼接、轉置、排序、比較、復制、重命名、刪除等 1.append語句 註:base數據集

JavaSE 學習筆記網絡編(二十三)

-c 可能 nbsp blog col accept 接收 存儲 pri 端口: 物理端口: 邏輯端口:用於標識進程的邏輯地址,不同進程的標識;有效端口:0~65535,其中0~1024系統使用或保留端口。 java 中ip對象:InetAddress. import

《SAS編與數據挖掘商業案例》學習筆記十一

ror otto -c ace mov 得到 replace 讀書筆記 集中 繼續讀書筆記,本文重點側重sas觀測值的操作方面, 主要包含:輸出觀測值、更新觀測值、刪除觀測值、停止輸出觀測值等

Java 學習筆記 Sleep停止線

run rgs ges xtend over exce http tac p s Sleep停止線程: 在Sleep狀態下被interrupt,interrupted 狀態會被擦除,返回false。 線程在Sleep狀態下被interrupt: public clas

Oracle學習筆記PL/SQL編

.cn 數據操作 dmi int 直接 字節 sql編程 gin number SQL(Structure Query Language)的含義是結構化查詢語句,最早由Boyce和Chambedin在1974年提出,稱為SEQUEL語言。1976年

Java 學習筆記安全

div ora mage 線程安全 cnblogs cor exception rup ron 線程安全: 線程安全的方法一定是排隊運行的。 public class SyncObject { synchronized public void met

java學習筆記初識多線

技術 運行 必須 dex ima this 認識 主線程 inf 初識多線程 一.進程的認識:   1.進程的理解:   進程可以理解成正在執行的任務,就是正在運行的程序,進程又分為兩種,一種是前臺進程,就是我們很直觀看見的,另一種是後臺進程,就是操作系統啟動就有的(系統級

java學習筆記網絡編

col client 應用場景 err pri 多圖片 data oca 網絡 網絡編程 一.網絡通信協議   1.網絡協議     不同的操作系統,不同的硬件設備上都能夠聯網,是因為互聯網設計規定了一套標準,這套標準就被稱為網絡協議,網絡協議規定了網絡傳輸的格式,速率和方