目次
序文
このプロジェクトでは、行列分解アルゴリズムを使用して、プレイヤーがプレイしたデータを詳細に分析します。その目的は、多くのゲームの中からプレイヤーに最適なゲームを選択し、比較的正確なゲーム推奨システムを実現することです。
まず、プロジェクトは、ゲーム名、ゲーム時間、ゲームの評価などの情報を含む、プレイヤーがプレイしたゲーム データを収集して分析します。これらのデータは、大規模なユーザーとゲームのインタラクション マトリックスを形成します。このマトリックスの各行はプレーヤーを表し、各列はゲームを表し、マトリックスの値はプレーヤーとゲームの間のインタラクションを表します。
次に、プロジェクトは行列分解アルゴリズムを使用して、疎なユーザー ゲーム行列を 2 つの小さな行列 (フィーチャー ゲーム行列とユーザー フィーチャー行列) に近似的に置き換えます。この分解プロセスにより、プレーヤーとゲームが潜在特徴空間にマッピングされ、プレーヤーとゲームの間の潜在的な関係を推測できるようになります。
モデルがトレーニングされると、システムはプレイヤーのゲーム履歴に基づいてプレイヤーが好む可能性のあるゲームを予測できます。この予測は、プレーヤーが他のプレーヤーとどの程度似ているか、およびゲームが他のゲームとどの程度似ているかに基づいています。その結果、システムは、各プレーヤーのゲームの好みや過去の行動を考慮して、各プレーヤーにパーソナライズされたゲームの推奨を提供できます。
全体として、このプロジェクトの目標は、行列分解と潜在因子モデルを通じて、より正確なゲーム推奨システムを提供することです。この種のパーソナライズされた推奨事項は、プレーヤーのゲーム体験を向上させることができ、また、ゲーム プラットフォームがより良いゲーム プロモーションを提供し、ユーザーの定着率を高めるのにも役立ちます。
全体的なデザイン
このパートには、システム全体の構成図とシステム フローチャートが含まれます。
全体システム構成図
システムの全体構成を図に示します。
システムフローチャート
システムフローを図に示します。
動作環境
この部分には、Python 環境、TensorFlow 環境、および PyQt5 環境が含まれます。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133148686#_38
モジュールの実装
このプロジェクトにはデータの前処理、モデルの構築、モデルの学習と保存、モデルのテストの 4 つのモジュールが含まれており、各モジュールの機能紹介と関連コードは以下のとおりです。
1. データの前処理
データ セットは Kaggle から提供されており、リンク アドレスはhttps://www.kaggle.com/tamber/steam-video-gamesです。このデータ セットには、ユーザーの ID、ゲーム名、購入するかプレイするか、およびゲーム期間が含まれます. その中には、合計 12,393 人のユーザーが含まれており、5,155 のゲームが含まれています。データセットをJupyter 作業パスの下のsteam-video-games
フォルダーに配置します。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133148686#1__97
2. モデルの構築
データをモデルにロードした後、モデル構造を定義し、損失関数を最適化する必要があります。
1) モデル構造を定義する
行列分解アルゴリズムを使用すると、ユーザー ゲームの疎行列は、フィーチャー ゲーム行列とユーザー フィーチャー行列という 2 つの小さな行列にほぼ置き換えられます。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133151049#1_54
2) 損失関数の最適化
L2 ノルムは、行列因数分解アルゴリズムの損失関数でよく使用されます。したがって、過学習を避けるために、このプロジェクトの損失関数には L2 ノルムも導入されています。Adagrad オプティマイザーを使用してモデル パラメーターを最適化します。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133151049#2_91
3. モデルのトレーニングと保存
本プロジェクトで使用したデータセットは、ゲームのDLC(ダウンロードコンテンツ、後発ダウンロードコンテンツ)を別のゲームとして記載しているため、精度を計算する際にはDLCとゲーム本体は同一ゲーム、同一シリーズと判断させていただきます。ゲームも同じものと判断できます。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133151049#3__105
1) モデルのトレーニング
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133151049#1_148
2) モデルの保存
モデルの使用を容易にするために、トレーニング結果を Joblib を使用して保存する必要があります。
詳細については、ブログを参照してください: https://blog.csdn.net/qq_31136513/article/details/133151049#2_187
4. モデルの適用
1 つ目は、ページのレイアウトを作成し、入力データを取得して確認することです。2 つ目は、取得したデータを以前に保存したモデルと照合して、アプリケーションの効果を実現することです。
1) ページを作成する
関連する操作は次のとおりです。
(1) コードを使用してページの基本レイアウトを描画し、Recommandation
クラスを作成します。
class Recommandation(QWidget):
#初始化
def __init__(self):
super().__init__()
self.initUI()
#初始化布局
def initUI(self):
#设置界面的初始位置和大小
self.setGeometry(600,200,450,550)
#窗口名
self.setWindowTitle('steam游戏推荐')
#设置组件,以下为标签
self.lb1 = QLabel('请输入游戏名:',self)
#这是所在位置
self.lb1.move(20,20)
self.lb2 = QLabel('请输入游戏名:',self)
self.lb2.move(20,80)
self.lb3 = QLabel('请输入游戏名:',self)
self.lb3.move(20,140)
self.lb4 = QLabel('请输入游戏名:',self)
self.lb4.move(20,200)
self.lb5 = QLabel('请输入游戏名:',self)
self.lb5.move(20,260)
#以下为下拉输入框的创建
self.combobox1 = QComboBox(self, minimumWidth=200)
self.combobox1.move(100,20)
self.combobox1.setEditable(True)
self.combobox2 = QComboBox(self, minimumWidth=200)
self.combobox2.move(100,80)
self.combobox2.setEditable(True)
self.combobox3 = QComboBox(self, minimumWidth=200)
self.combobox3.move(100,140)
self.combobox3.setEditable(True)
self.combobox4 = QComboBox(self, minimumWidth=200)
self.combobox4.move(100,200)
self.combobox4.setEditable(True)
self.combobox5 = QComboBox(self, minimumWidth=200)
self.combobox5.move(100,260)
self.combobox5.setEditable(True)
#以下为输入的按键设置
self.bt1 = QPushButton('请输入游戏时间',self)
self.bt1.move(330,20)
self.bt2 = QPushButton('请输入游戏时间',self)
self.bt2.move(330,80)
self.bt3 = QPushButton('请输入游戏时间',self)
self.bt3.move(330,140)
self.bt4 = QPushButton('请输入游戏时间',self)
self.bt4.move(330,200)
self.bt5 = QPushButton('请输入游戏时间',self)
self.bt5.move(330,260)
#推荐按钮
self.bt=QPushButton('推荐开始',self)
self.bt.move(20,400)
#初始化下拉输入框
self.init_combobox()
#连接按键与槽
self.bt1.clicked.connect(self.timeDialog)
self.bt2.clicked.connect(self.timeDialog)
self.bt3.clicked.connect(self.timeDialog)
self.bt4.clicked.connect(self.timeDialog)
self.bt5.clicked.connect(self.timeDialog)
#连接推荐
self.bt.clicked.connect(self.recommand)
connect()
Qt 独自のシグナルとスロットの仕組みで、スロットがシグナルを受信して処理します。ここでは Clicked がシグナルとして使用されており、ボタンをクリックするとシグナルが送信されます。
(2) ドロップダウン入力ボックスを初期化し、ドロップダウン ボックスのメニューにゲームリストを入力し、オートコンプリート機能を追加します。
#初始化下拉输入框
def init_combobox(self):
#增加选项元素
for i in range(len(gamelist)):
self.combobox1.addItem(gamelist[i])
self.combobox2.addItem(gamelist[i])
self.combobox3.addItem(gamelist[i])
self.combobox4.addItem(gamelist[i])
self.combobox5.addItem(gamelist[i])
self.combobox1.setCurrentIndex(-1)
self.combobox2.setCurrentIndex(-1)
self.combobox3.setCurrentIndex(-1)
self.combobox4.setCurrentIndex(-1)
self.combobox5.setCurrentIndex(-1)
#增加自动补全
self.completer = QCompleter(gamelist)
#补全方式
self.completer.setFilterMode(Qt.MatchStartsWith)
self.completer.setCompletionMode(QCompleter.PopupCompletion)
self.combobox1.setCompleter(self.completer)
self.combobox2.setCompleter(self.completer)
self.combobox3.setCompleter(self.completer)
self.combobox4.setCompleter(self.completer)
self.combobox5.setCompleter(self.completer)
(3) スロットの設定とデータの保存を同時に行う
関連する操作は次のとおりです。
def timeDialog(self):
#获取信号
sender = self.sender()
if sender == self.bt1:
#获取下拉输入框1输入的游戏名
gamename = self.combobox1.currentText()
#通过字典game2idx查询获得的游戏名所对应的序列号
gameid = game2idx.get(gamename)
#没有序列号的情况,可以理解为未输入正确的游戏名,或者输入为空
if gameid == None:
#这种情况下生成一个MessageBox报错
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
#输入正确的情况,将游戏名字、ID,分别记录到一个字典里,方便保存与更改
gamedict[1] = gamename
idxdict[1] = gameid
#弹出一个文本输入框,要求输入对应游戏时长
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
#如果输入正确,将时长记录到一个字典中,方便保存与更改
if ok:
timedict[1] = text
elif sender == self.bt2:
gamename = self.combobox2.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[2] = gamename
idxdict[2] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[2] = text
elif sender == self.bt3:
gamename = self.combobox3.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[3] = gamename
idxdict[3] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[3] = text
elif sender == self.bt4:
gamename = self.combobox4.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[4] = gamename
idxdict[4] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[4] = text
elif sender == self.bt5:
gamename = self.combobox5.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[5] = gamename
idxdict[5] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[5] = text
(4) データが入力されているかを確認し、モデルを呼び出す準備をします。
def recommand(self):
#验证是否存在没有写入的数据
c = 0
for i in range(1,6):
if gamedict[i] == "NULL":
c+=1
if idxdict[i] == "NULL":
c+=1
if timedict[i] == "NULL":
c+=1
#全部写完的情况
if c == 0:
#将字典转化为列表
usertime = list(timedict.values())
useridx = list(idxdict.values())
#调用模型
allrecidx = UserSimilarity(useridx,usertime)
#降序排列数据
rr = np.argsort(-allrecidx)
#获取排行前五的游戏ID
top_k = rr[:5]
#将ID对应的游戏名字输入数组
for i in top_k:
recgame.append(idx2game[i])
#将数组转化为字符串并输出
reclist = ','.join(recgame)
reply = QMessageBox.information(self,'推荐的游戏','给您推荐的游戏是'+reclist, QMessageBox.Close)
#存在没有写完的数据,要求重新写入
else:
reply = QMessageBox.information(self,'Error','请输入全部数据!', QMessageBox.Close)
2) モデルのインポートと呼び出し
関連する操作は次のとおりです。
(1) 現在のフォルダーに Save_data モデルをロードします
game2idx = joblib.load('./Save_data/game2idx.pkl')
idx2game = joblib.load('./Save_data/idx2game.pkl')
rec = joblib.load('./Save_data/rec.pkl')
hours = joblib.load('./Save_data/hours.pkl')
buy = joblib.load('./Save_data/buy.pkl')
users = joblib.load('./Save_data/buyers.pkl')
(2) Qt で収集したデータと出力として訓練されたユーザーの間で最も類似性の高いデータを記述するユーザー類似度関数を作成します
def UserSimilarity(games, game_hours):
similarity = np.zeros(len(users)) # 用户相似度矩阵
for i in range(len(users)):
#计算用户输入的游戏与数据集中每个用户购买游戏的重合度
coincidence = 0 #重合度,每重合一个游戏加1
positions = [] #重合游戏在games中的位置
#获取数据集中的第i个玩家与用户输入的重合情况
for ii in range(len(games)):
if games[ii] in np.where(buy[users[i], :] == 1)[0]:
coincidence += 1
positions.append(ii)
#如果没有重合,则相似度为0,跳过
if coincidence == 0:
continue
simi = []
#将重合的游戏,根据时长和相同游戏的时长差取绝对值,根据e^-x计算出相似度
for position in positions:
game = games[position]
hour = abs(game_hours[position] - hours[users[i], game])
simi.append(math.exp(-hour))
#对所有相似度取均值,得到用户与数据集中第i个玩家的相似度similarity[i]
similarity[i] = sum(simi) / coincidence
#相似度与玩家—游戏矩阵每一行相乘
for i in range(len(users)):
user = users[i]
rec[user] = rec[user] * similarity[i]
new_rec = np.zeros(len(rec[0])) # 1*n_games矩阵
#将玩家—游戏矩阵按列相加,得到用户对每个游戏的喜好程度,即new_rec矩阵
for i in range(len(new_rec)):
for user in users:
new_rec[i] += rec[user][int(i)]
return new_rec
3) モデルアプリケーションコード
関連するコードは次のとおりです。
import joblib
import numpy as np
import pandas as pd
import math
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
#读取数据
game2idx = joblib.load('./Save_data/game2idx.pkl')
idx2game = joblib.load('./Save_data/idx2game.pkl')
rec = joblib.load('./Save_data/rec.pkl')
hours = joblib.load('./Save_data/hours.pkl')
buy = joblib.load('./Save_data/buy.pkl')
users = joblib.load('./Save_data/buyers.pkl')
#游戏名称列表
gamelist = list(game2idx)
#游戏数
n_game = len(gamelist)
#传入字典
gamedict = {
1:"NULL",2:"NULL",3:"NULL",4:"NULL",5:"NULL"}
timedict = {
1:"NULL",2:"NULL",3:"NULL",4:"NULL",5:"NULL"}
idxdict = {
1:"NULL",2:"NULL",3:"NULL",4:"NULL",5:"NULL"}
#下面两个是要传递的
usertime=[]
useridx=[]
#下面是返回的推荐游戏
recgame=[]
#相似度推荐
def UserSimilarity(games, game_hours):
similarity = np.zeros(len(users)) #用户相似度矩阵
for i in range(len(users)):
#计算用户输入的游戏与数据集中每个用户购买游戏的重合度
coincidence = 0 #重合度
positions = [] #重合游戏在games中的位置
for ii in range(len(games)):
if games[ii] in np.where(buy[users[i], :] == 1)[0]:
coincidence += 1
positions.append(ii)
if coincidence == 0:
continue
simi = []
for position in positions:
game = games[position]
hour = abs(game_hours[position] - hours[users[i], game])
simi.append(math.exp(-hour))
similarity[i] = sum(simi) / coincidence
#相似度与玩家—游戏矩阵每一行相乘
for i in range(len(users)):
user = users[i]
rec[user] = rec[user] * similarity[i]
new_rec = np.zeros(len(rec[0])) #1*n_games矩阵
for i in range(len(new_rec)):
for user in users:
new_rec[i] += rec[user][int(i)]
return new_rec
class Recommandation(QWidget):
#初始化
def __init__(self):
super().__init__()
self.initUI()
#初始化布局
def initUI(self):
#设置界面的初始位置和大小
self.setGeometry(600,200,450,550)
#窗口名
self.setWindowTitle('steam游戏推荐')
#设置组件,以下为标签
self.lb1 = QLabel('请输入游戏名:',self)
#这是所在位置
self.lb1.move(20,20)
self.lb2 = QLabel('请输入游戏名:',self)
self.lb2.move(20,80)
self.lb3 = QLabel('请输入游戏名:',self)
self.lb3.move(20,140)
self.lb4 = QLabel('请输入游戏名:',self)
self.lb4.move(20,200)
self.lb5 = QLabel('请输入游戏名:',self)
self.lb5.move(20,260)
#以下为下拉输入框的创建
self.combobox1 = QComboBox(self, minimumWidth=200)
self.combobox1.move(100,20)
self.combobox1.setEditable(True)
self.combobox2 = QComboBox(self, minimumWidth=200)
self.combobox2.move(100,80)
self.combobox2.setEditable(True)
self.combobox3 = QComboBox(self, minimumWidth=200)
self.combobox3.move(100,140)
self.combobox3.setEditable(True)
self.combobox4 = QComboBox(self, minimumWidth=200)
self.combobox4.move(100,200)
self.combobox4.setEditable(True)
self.combobox5 = QComboBox(self, minimumWidth=200)
self.combobox5.move(100,260)
self.combobox5.setEditable(True)
#以下为输入的按键设置
self.bt1 = QPushButton('请输入游戏时间',self)
self.bt1.move(330,20)
self.bt2 = QPushButton('请输入游戏时间',self)
self.bt2.move(330,80)
self.bt3 = QPushButton('请输入游戏时间',self)
self.bt3.move(330,140)
self.bt4 = QPushButton('请输入游戏时间',self)
self.bt4.move(330,200)
self.bt5 = QPushButton('请输入游戏时间',self)
self.bt5.move(330,260)
#推荐按钮
self.bt=QPushButton('推荐开始',self)
self.bt.move(20,400)
#初始化下拉输入框
self.init_combobox()
#连接按键与槽
self.bt1.clicked.connect(self.timeDialog)
self.bt2.clicked.connect(self.timeDialog)
self.bt3.clicked.connect(self.timeDialog)
self.bt4.clicked.connect(self.timeDialog)
self.bt5.clicked.connect(self.timeDialog)
#连接推荐
self.bt.clicked.connect(self.recommand)
#初始化下拉输入框
def init_combobox(self):
#增加选项元素
for i in range(len(gamelist)):
self.combobox1.addItem(gamelist[i])
self.combobox2.addItem(gamelist[i])
self.combobox3.addItem(gamelist[i])
self.combobox4.addItem(gamelist[i])
self.combobox5.addItem(gamelist[i])
self.combobox1.setCurrentIndex(-1)
self.combobox2.setCurrentIndex(-1)
self.combobox3.setCurrentIndex(-1)
self.combobox4.setCurrentIndex(-1)
self.combobox5.setCurrentIndex(-1)
#增加自动补全
self.completer = QCompleter(gamelist)
#补全方式
self.completer.setFilterMode(Qt.MatchStartsWith)
self.completer.setCompletionMode(QCompleter.PopupCompletion)
self.combobox1.setCompleter(self.completer)
self.combobox2.setCompleter(self.completer)
self.combobox3.setCompleter(self.completer)
self.combobox4.setCompleter(self.completer)
self.combobox5.setCompleter(self.completer)
def timeDialog(self):
#获取信号
sender = self.sender()
if sender == self.bt1:
#获取下拉输入框1输入的游戏名
gamename = self.combobox1.currentText()
#通过字典game2idx查询获得的游戏名所对应的序列号
gameid = game2idx.get(gamename)
#没有序列号的情况,可以理解为未输入正确的游戏名,或者输入为空
if gameid == None:
#这种情况下生成一个MessageBox报错
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
#输入正确的情况,将游戏名字、ID,分别记录到一个字典里,方便保存与更改
gamedict[1] = gamename
idxdict[1] = gameid
#弹出一个文本输入框,要求输入对应的游戏时长
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
#如果输入正确,将时长记录到一个字典中,方便保存与更改
if ok:
timedict[1] = text
elif sender == self.bt2:
gamename = self.combobox2.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[2] = gamename
idxdict[2] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[2] = text
elif sender == self.bt3:
gamename = self.combobox3.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[3] = gamename
idxdict[3] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[3] = text
elif sender == self.bt4:
gamename = self.combobox4.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[4] = gamename
idxdict[4] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[4] = text
elif sender == self.bt5:
gamename = self.combobox5.currentText()
gameid = game2idx.get(gamename)
if gameid == None:
reply = QMessageBox.information(self,'Error','请输入正确的游戏名!', QMessageBox.Close)
else:
gamedict[5] = gamename
idxdict[5] = gameid
text, ok = QInputDialog.getDouble(self, '游戏时间', '请输入游戏时间:', min = 0.1)
if ok:
timedict[5] = text
def recommand(self):
#验证是否存在没有写入的数据
c = 0
for i in range(1,6):
if gamedict[i] == "NULL":
c+=1
if idxdict[i] == "NULL":
c+=1
if timedict[i] == "NULL":
c+=1
#全部写完的情况
if c == 0:
#将字典转化为列表
usertime = list(timedict.values())
useridx = list(idxdict.values())
#调用模型
allrecidx = UserSimilarity(useridx,usertime)
#降序排列数据
rr = np.argsort(-allrecidx)
#获取排行前五的游戏ID
top_k = rr[:5]
#将ID对应的游戏名字输入数组
for i in top_k:
recgame.append(idx2game[i])
#将数组转化为字符串并输出
reclist = ','.join(recgame)
reply = QMessageBox.information(self,'推荐的游戏','给您推荐的游戏是'+reclist, QMessageBox.Close)
#存在没有写完的数据,要求重新写入
else:
reply = QMessageBox.information(self,'Error','请输入全部数据!', QMessageBox.Close)
#主函数
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Recommandation()
w.show()
sys.exit(app.exec_())
関連する他のブログ
プロジェクトのソースコードのダウンロード
詳細については、私のブログ リソースのダウンロード ページをご覧ください。
その他の情報をダウンロードする
人工知能に関連する学習ルートと知識システムを引き続き理解したい場合は、私の別のブログ「ヘビー級 | 完全な人工知能 AI 学習 - 基礎知識の学習ルート」を参照してください。すべての情報はネットワーク ディスクから直接ダウンロードできます。》
このブログは、Github の有名なオープンソース プラットフォーム、AI テクノロジー プラットフォーム、および関連分野の専門家 (Datawhale、ApacheCN、AI Youdao、Dr. Huang Haiguang など) について言及しており、約 100G の関連情報があります。私の友人全員を助けることができれば幸いです。