前回のブログ記事で統計的なレコメンド部分が終わりましたので、今回はVue+Element-ui+SpringBootを使って簡単にシステムを構築し、動画を表示し、パーソナライズされたレコメンド部分を紹介していきます。
1 システムページのデザイン
当初は、Douban に似た映画レコメンデーション システムを設計したいと考えていました。
- ログインするとハイスコアムービーが視聴可能
- おすすめの映画が見れる
- 評価できる
1.1 フロントエンドテンプレートのダウンロード
- 時間の関係で、ここではDouban のムービー システムを模倣したテンプレートを選択しましたが、本来の目的は Vue の能力を発揮することではなく、できるだけシンプルに実行する方法です。
- 次に、システムを変更し、Element-ui を使用して迅速な開発を行います。
1.2 バックエンドシステム構築
- SpringBoot を使用して迅速な開発を実現する
- MongoDB の関連する依存関係を追加し、データの取得が成功したかどうかをテストするインターフェイスを作成します。
- テストが成功すると、Vue は axios 関連のコードを作成します
注: バージョンの問題には必ず注意してください。エラーを報告すると非常に悲しいことになります...
data:
mongodb:
host: 服务器IP
port: 27017
database: recommender
username: "root"
password: "123456"
2. 潜在意味モデルに基づく協調フィルタリングアルゴリズム
ユーザー行動分析に基づく推奨アルゴリズムは、一般に協調フィルタリング アルゴリズムと呼ばれます。いわゆる協調フィルタリングとは、多くのユーザーが Web サイトとの継続的な対話を通じて協力して作業できるため、推奨リストから興味のない項目を継続的に除外して、ユーザーのニーズをさらに満たすことができることを意味します。一般的な実装方法には次のものがあります。
- 近隣ベースの手法
- 潜在意味論モデル
- グラフベースのランダム ウォーク アルゴリズム
私たちは、潜在的なセマンティック モデル (LFM) を使用します。その中心的なアイデアは、潜在的な要素をマイニングすることによって推奨タスクを完了することです。将来的にはこれを改善していきます。
主な手順は次のとおりです。
- UserId と MovieID のデカルト積で (uid, Mid) のタプルを生成します。
- モデルによって予測された (uid, Mid) のタプル。
- 予測結果を予測スコア順に並べ替えます。
- 現在のユーザーへの推奨として最高スコアを持つ K 個の映画を返します。
- 映画の類似性は ALS によって計算され、MongoDB データベースに保存され、後でリアルタイムで推奨されるように準備されます。
// 核心程序
// 从rating数据中提取所有的uid和mid,并去重
val userRDD = ratingRDD.map(_._1).distinct()
val movieRDD = ratingRDD.map(_._2).distinct()
// 训练隐语义模型
val trainData = ratingRDD.map( x => Rating(x._1, x._2, x._3) )
val (rank, iterations, lambda) = (200, 5, 0.1)
val model = ALS.train(trainData, rank, iterations, lambda)
// 基于用户和电影的隐特征,计算预测评分,得到用户的推荐列表
// 计算user和movie的笛卡尔积,得到一个空评分矩阵
val userMovies = userRDD.cartesian(movieRDD)
// 调用model的predict方法预测评分
val preRatings = model.predict(userMovies)
val userRecs = preRatings
.filter(_.rating > 0) // 过滤出评分大于0的项
.map(rating => ( rating.user, (rating.product, rating.rating) ) )
.groupByKey()
.map{
case (uid, recs) => UserRecs( uid, recs.toList.sortWith(_._2>_._2).take(USER_MAX_RECOMMENDATION).map(x=>Recommendation(x._1, x._2)) )
}
.toDF()
userRecs.write
.option("uri", mongoConfig.uri)
.option("collection", USER_RECS)
.mode("overwrite")
.format("com.mongodb.spark.sql")
.save()
// 基于电影隐特征,计算相似度矩阵,得到电影的相似度列表
val movieFeatures = model.productFeatures.map{
case (mid, features) => (mid, new DoubleMatrix(features))
}
// 对所有电影两两计算它们的相似度,先做笛卡尔积
val movieRecs = movieFeatures.cartesian(movieFeatures)
.filter{
// 把自己跟自己的配对过滤掉
case (a, b) => a._1 != b._1
}
.map{
case (a, b) => {
val simScore = this.consinSim(a._2, b._2)
( a._1, ( b._1, simScore ) )
}
}
.filter(_._2._2 > 0.8) // 过滤出相似度大于0.8的
.groupByKey()
.map{
case (mid, items) => MovieRecs( mid, items.toList.sortWith(_._2 > _._2).map(x => Recommendation(x._1, x._2)) )
}
.toDF()
movieRecs.write
.option("uri", mongoConfig.uri)
.option("collection", MOVIE_RECS)
.mode("overwrite")
.format("com.mongodb.spark.sql")
.save()
ただし、この方法には次のような欠点があります。
- リアルタイムのレコメンデーションを実現することは困難です。
- レコメンデーション モデルの更新にはユーザーの行動記録を反復処理する必要があり、各トレーニングには時間がかかります。
- コールドスタートの問題は明らかです。