K-近鄰算法預測電影類型

K-近鄰算法預測電影類型

k-近鄰算法是一種比較簡單,但是在一些方面又有很多作用的算法,比較常用的就是推薦入住位置,或者推薦入住酒店等等。

K-近鄰算法的原理:就是根據特徵值,計算出離自己最近的那個分類,自己也屬於那個類別,K-近鄰是一種分類的算法模型。

K-近鄰算法的公式:(a1-b1)2 + (a2-b2)2 + (a3-b3)2 A(a1,a2,a3) B (b1,b2,b3)
A代表了已知的數據,B代表未知的數據,得出的結果就是未知的數據與已知的距離,也稱爲歐式距離

獲取數據

在機器學習中數據是非常重要的,是取決你的模型是否能夠成功的建立起來的重要因素。在這裏我的數據是自己在豆瓣上面爬取出來的,如果不懂的爬蟲的朋友可以自行百度,或者向我拿數據也是可以的。

| 在這裏插入圖片描述
這些就是數據,有演員、導演、票房等等。

處理數據

在機器學習中,機器只會識別數字,不能識別字符,所以我們要把這些數據都轉換爲數字,首先是轉換票房,因爲這裏的票房是有億、萬這些字眼的,所以我們把他轉化爲數字。

mobie_box_list = []
for i in all_data["mobie_box"]:
    if "億" in i:
        mobie_box_list.append(float(re.match(r"\d.*\d+",i).group()) * 100000000)
    if "萬" in i:
        mobie_box_list.append(float(re.match(r"\d.*\d+", i).group()) * 10000)
all_data["mobie_box"] = mobie_box_list
all_data["mobie_box"] = all_data["mobie_box"].astype("int")

因爲這裏的每部電影的電影類型都不止一個,所以我們只提取一個類型來預測比較好。

all_data["type"] = [ "".join(i[1:2]) if len(i) > 1 else "".join(i[:1]) for i in all_data["type"].str.split("、")]
print(all_data["type"])

這裏有些電影的導演不止一個人,所以我們要把導演給分隔出來,就是有些電影有兩個導演,那我們就把導演給分隔出來,然後複製電影信息,然後把分隔出來的導演放進去。

all_data=all_data.drop('director', axis=1).join(all_data['director'].str.split(' / ', expand=True).stack().reset_index(level=1, drop=True).rename('director'))

對地區進行分類處理

n = 0
dict_10 = {}
country_list = list(set(all_data["country"].tolist()))
for z in country_list:
    dict_10[z] = n
    n +=1
print(dict_10)
all_data["country"] = all_data["country"].map(dict_10)

因爲暫時沒想到比較好的辦法來處理演員數據,所以在模型建立的時候,把演員從特徵值中刪除掉。

數據處理源代碼

'''
K-近鄰預測電影類型
creat on July 21,2019
@Author 小明
'''
'''
1、處理數據
2、特徵工程
3、數據集劃分
4、建立模型
5、調整模型
'''
import pandas as pd
from matplotlib.pylab import date2num
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.feature_extraction import DictVectorizer
from sklearn.preprocessing import FunctionTransformer
import datetime
import re
pd.set_option("display.max_columns",1000)
pd.set_option("display.width",500)
pd.set_option("display.max_colwidth",10000)

#處理數據
data_1 = pd.read_csv("I:/crack/DATA/movie_datadouban.csv")
data_1.index.name = "id"
data_2 = pd.read_csv("I:/crack/DATA/movie_datadouban_2.csv")
data_3 = pd.read_csv("I:/crack/DATA/movie_datadouban_3.csv")
data = pd.merge(data_3,data_2,how="outer")
all_data = pd.merge(data,data_1,how="outer")

data_4 = pd.read_csv("I:/crack/DATA/movie_data2009.csv")
data_5 = pd.read_csv("I:/crack/DATA/movie_data2010.csv")
data_6 = pd.read_csv("I:/crack/DATA/movie_data2011.csv")
data_7 = pd.read_csv("I:/crack/DATA/movie_data2012.csv")
data_8 = pd.read_csv("I:/crack/DATA/movie_data2013.csv")
data_9 = pd.read_csv("I:/crack/DATA/movie_data2014.csv")
data_10 = pd.read_csv("I:/crack/DATA/movie_data2015.csv")
data_11 = pd.read_csv("I:/crack/DATA/movie_data2016.csv")
data_12 = pd.read_csv("I:/crack/DATA/movie_data2017.csv")
data_13 = pd.read_csv("I:/crack/DATA/movie_data2018.csv")
d_1 = pd.merge(data_4,data_5,how="outer")
d_2 = pd.merge(d_1,data_6,how="outer")
d_3 = pd.merge(d_2,data_7,how="outer")
d_4 = pd.merge(d_1,data_8,how="outer")
d_5 = pd.merge(data_9,data_10,how="outer")
d_6 = pd.merge(data_11,data_12,how="outer")
d_7 = pd.merge(d_4,d_5,how="outer")
d_8 = pd.merge(d_7,d_6,how="outer")
d_9 = pd.merge(d_8,data_13,how="outer")
all_d = d_9.loc[:,["title","mobie_box","history_rank"]]
all_data = pd.merge(all_data,all_d,on=["title","title"])
pd.DataFrame.to_csv(all_data,"I:/crack/DATA/all_movies.csv",encoding="utf_8_sig")

#轉換票房數據
mobie_box_list = []
for i in all_data["mobie_box"]:
    if "億" in i:
        mobie_box_list.append(float(re.match(r"\d.*\d+",i).group()) * 100000000)
    if "萬" in i:
        mobie_box_list.append(float(re.match(r"\d.*\d+", i).group()) * 10000)
all_data["mobie_box"] = mobie_box_list
all_data["mobie_box"] = all_data["mobie_box"].astype("int")

#截取電影類型
all_data["type"] = [ "".join(i[1:2]) if len(i) > 1 else "".join(i[:1]) for i in all_data["type"].str.split("、")]
print(all_data["type"])


#把導演分開出來
all_data=all_data.drop('director', axis=1).join(all_data['director'].str.split(' / ', expand=True).stack().reset_index(level=1, drop=True).rename('director'))
print(all_data)

#把演員轉換成列表形式
# all_data = all_data.drop('actor', axis=1).join(all_data['actor'].str.split(' / ', expand=True).stack().reset_index(level=1, drop=True).rename('actor'))
# print(all_data)

#時間戳處理
# time = pd.DatetimeIndex(all_data["release_time"])
# all_data["year"] = time.year
# all_data["month"] = time.month
# all_data["day"] = time.day
# all_data = all_data.drop(["release_time"],axis = 1)
# all_data = all_data.drop(["actor"],axis = 1)
all_data["release_time"] = pd.to_datetime(all_data["release_time"])
all_data["release_time"] = date2num(all_data["release_time"])
all_data["release_time"] = all_data["release_time"].astype("int")

#特徵工程
vector = DictVectorizer(sparse=False)
director_list = []
# for z in all_data["director"]:
#     dict = {}
#     dict["director"] = z
#     director_list.append(dict)
# director = vector.fit_transform(director_list)
# all_data["director"] = director

#將地區進行分類
n = 0
dict_10 = {}
country_list = list(set(all_data["country"].tolist()))
for z in country_list:
    dict_10[z] = n
    n +=1
print(dict_10)
all_data["country"] = all_data["country"].map(dict_10)

#對電影類型進行分類處理
# j = 0
# dict_9 = {}
# country_list = list(set(all_data["type"].tolist()))
# for z in country_list:
#     dict_9[z] = j
#     j +=1
# print(dict_9)
# all_data["type"] = all_data["type"].map(dict_9)

d = 0
dict_8 = {}
country_list = list(set(all_data["director"].tolist()))
for z in country_list:
    dict_8[z] = d
    d +=1
print(dict_8)
all_data["director"] = all_data["director"].map(dict_8)
pd.DataFrame.to_csv(all_data,"I:/crack/DATA/K-movie.csv",encoding="utf_8_sig")

模型建立

模型建立的步驟
1、讀取數據
2、劃分特徵值和目標值
3、標準化處理
4、劃分數據集
5、建立模型
6、模型評估

讀取數據和劃分特徵值和目標值

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA

data = pd.read_csv("I:crack/DATA/K-movie.csv")

#劃分數據集
target = data["type"]
featrue = data.loc[:,["comment_number","country","play_time","release_time","score","mobie_box","history_rank","director"]]

標準化處理

因爲防止某一特徵值的數據對目標值的影響過大,所以要進行標準化處理

st = StandardScaler()
featrue = st.fit_transform(featrue)

劃分測試集和訓練集

train_x,test_x,train_y,test_y = train_test_split(featrue,target,test_size=0.25)

train_x就是訓練集的特徵值
test_x就是測試集的特徵值
train_y就是訓練集的目標值
test_y就是測試集的目標值

test_size就是代表測試集佔的比例

建立模型

kn = KNeighborsClassifier(n_neighbors=6,algorithm="auto")
kn.fit(train_x,train_y)

predict_y = kn.predict(test_x)

score = kn.score(test_x,test_y)

print(score)

n_neighbors代表提取分數最下的幾個來預測,algorithm是用來對數據結構進行調優的,默認都是auto的

運行得出來的結果

在這裏插入圖片描述
得出來的準確率只有0.365,而且每次運行的準確率都是不一樣的,那是因爲每次測試集和訓練集都是隨機分隔的,所以結果不一樣。

交叉驗證

上面得出準確率這麼低,難道這個項目就失敗了嗎?模型就失敗了嗎?不一定,因爲還有一個K值的調優,只有我們把K值調到最優才能看到模型的準確率

交叉驗證的原理:交叉驗證就是先把訓練集分爲n等份,然後提取一部分當做驗證集,然後對驗證集進行預測,每次預測的結果都保留下來,然後把這些結果進行取平均值,因爲有些模型是需要傳遞超參數的,比如K-近鄰算法的K值,然後把每個可能的K-值放進去一一驗證,就會得出最好的超參數的值,
網格搜索:網格搜索就是,有一些模型的超參數不知一個,然後網格搜索就會把這些參數都一一驗證,得出最好的超參數

注意:交叉驗證也可以得出測試集的預測值,和預測結果

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA
from sklearn.model_selection import GridSearchCV

data = pd.read_csv("I:crack/DATA/K-movie.csv")

#劃分數據集
target = data["type"]
featrue = data.loc[:,["comment_number","country","play_time","score","mobie_box","history_rank","director"]]
print(target)

#標準化
st = StandardScaler()
featrue = st.fit_transform(featrue)

#劃分數據集
train_x,test_x,train_y,test_y = train_test_split(featrue,target,test_size=0.25)

#特徵工程標準化
sta = StandardScaler()
train_x = sta.fit_transform(train_x)
test_x = sta.fit_transform(test_x)

#建立模型
kn = KNeighborsClassifier()
# kn.fit(train_x,train_y)
#
# predict_y = kn.predict(test_x)
#
# score = kn.score(test_x,test_y)
# print(score)

#建立交叉驗證的對象
gri = GridSearchCV(kn,param_grid={"n_neighbors":[1,2,3,4,5,6,7,8,9],"algorithm":["auto","ball_tree","kd_tree"]},cv=10)

#這裏把全部數據都當做訓練集,因爲數據量不多
gri.fit(featrue,target)

#打印測試集的準確率,這個值是所有結果中最好的結果,就是每個參數10次運行結果的最好結果
print(gri.score(test_x,test_y))

#打印最好的驗證結果,就是最好的準確率平均值
print(gri.best_score_)

#打印最好的參數
print(gri.best_estimator_)

# 打印每個參數的每次預測的結果
print(gri.cv_results_)

打印最好結果的平均值
在這裏插入圖片描述

打印最好的參數值
KNeighborsClassifier(algorithm=‘auto’, leaf_size=30, metric=‘minkowski’,
metric_params=None, n_jobs=None, n_neighbors=3, p=2,
weights=‘uniform’)

召回率和精準率和F1值

上面看到最好的平均值結果才0.31,難道這個模型就徹底沒救了嗎?也不是,我可以來看來每個分類的召回率和精準率和F1值

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.decomposition import PCA

data = pd.read_csv("I:crack/DATA/K-movie.csv")

#劃分數據集
target = data["type"]
featrue = data.loc[:,["comment_number","country","play_time","score","mobie_box","history_rank","director"]]


#標準化
st = StandardScaler()
featrue = st.fit_transform(featrue)

#劃分數據集
train_x,test_x,train_y,test_y = train_test_split(featrue,target,test_size=0.25)

#特徵工程標準化
sta = StandardScaler()
train_x = sta.fit_transform(train_x)
test_x = sta.fit_transform(test_x)

#建立模型
kn = KNeighborsClassifier(n_neighbors=5,algorithm="auto")
kn.fit(train_x,train_y)

predict_y = kn.predict(test_x)

score = kn.score(test_x,test_y)

class_data = list(set(test_y.tolist()))
print(class_data)
all_class = list(set(target.tolist()))
#傳遞的參數,y_true傳遞的是測試集的目標值,y_pred傳遞的是預測結果,target_names傳遞的是測試集的目標值的分類數據,labels傳遞的是分類索引的索引列表,也就是說target_names上面顯示的名稱都是從labels裏面得來的。
print(classification_report(y_true=test_y,y_pred=predict_y,target_names=class_data,labels=class_data))

#返回的結果就是精確值、召回率、F1值、和每個類別在測試集中的真實數量

動作 0.24 0.36 0.29 11
歌舞 0.00 0.00 0.00 2
災難 0.00 0.00 0.00 1
愛情 0.50 0.14 0.22 7
懸疑 0.00 0.00 0.00 5
喜劇 0.67 0.33 0.44 6
犯罪 0.00 0.00 0.00 3
同性 0.00 0.00 0.00 1
科幻 0.29 0.25 0.27 8
動畫 0.50 0.92 0.65 12
劇情 0.29 0.50 0.36 4
音樂 0.00 0.00 0.00 1
西部 0.00 0.00 0.00 1
武俠 0.00 0.00 0.00 1
傳記 0.17 0.25 0.20 4
奇幻 0.00 0.00 0.00 4
冒險 0.17 1.00 0.29 1
家庭 0.00 0.00 0.00 4
驚悚 0.00 0.00 0.00 4

這裏可以看到這個模型對動畫和冒險者這兩個分類還是效果的,所以也證明了這個模型也不是一文不值的。

總結

這個模型的準確率這麼低,不外乎幾個原因:

1、數據量太小,不足以用來預測和分析
2、數據的價值太低,預測的結果不理想
3、數據處理的方法太差,沒能發揮數據的價值,就像演員數據不能發揮作用
4、特徵工程的處理方法不好,所以導致模型結果不理想。

我相信只要解決掉上面的問題,模型的準確率也會大大提高的。

有什麼不懂的地方可以加我的QQ 1693490575 問我,或者有什麼好的建議或者模型方法,也歡迎跟我探討一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章