這里寫目錄標題
- 異構圖?
- 處理數據:
異構圖?
異構圖:就是指節點與邊類型不同的圖。
連接預測:目的是預測圖中兩個節點之間是否存在一條邊,或者是預測兩個節點之間,在未來可能形成的連接。
eg:
節點:
研究人員A、研究人員B、研究人員C
論文P1、論文P2
機構I1
邊關系:
研究人員A 寫作 論文P1
研究人員B 寫作 論文P1
研究人員C 寫作 論文P2
論文P1 隸屬于 機構I1
例如呢,我們想預測 在未來 A 與B 是否會合作寫作論文呢?
或者是預測 B會不會加入機構l1呢?
處理數據:
代碼展示,其中包括我其中遇到的困惑。
"""
MoviesLens數據集:描述了MoviesLens的評分以及標記活動。
該數據集包括600多個用戶對9000多部電影的10萬個評分。
使用該數據集生成兩種節點類型: 分別保存電影 和 用戶的數據,
以及一種連接用戶和電影的邊緣類型,表示用戶是否對特定電影進行了評級關系。
最后,鏈接預測任務 嘗試預測缺失的評分,可以用于向用戶推薦新電影。"""import torch
import os
import pandas as pd
from torch_geometric.data import HeteroData
import torch_geometric.transforms as T
# 電影
movies_path = './data/ml-latest-small/movies.csv'
# 評分
ratings_path = './data/ml-latest-small/ratings.csv'# 在處理數據之前肯定得先知道csv中的數據格式
# print('movies.csv')
# print('movies.csv:')
# print('===========')
# print(pd.read_csv(movies_path)[["movieId", "genres"]].head(10))
# print()
# print('ratings.csv:')
# print('============')
# print(pd.read_csv(ratings_path)[["userId", "movieId"]].head(10))# 加載數據,movieId 作為索引列
movies_df = pd.read_csv(movies_path,index_col='movieId')
# data = {
# 'movieId': [1, 2, 3],
# 'title': ['Toy Story', 'Jumanji', 'Grumpier Old Men'],
# 'genres': ['Adventure|Animation|Children|Comedy|Fantasy',
# 'Adventure|Children|Fantasy',
# 'Comedy|Romance']
# }
# 執行下方這行代碼,作用就是按照 | 進行分割,且使用one-hot 編碼
# 輸出: Adventure Animation Children Comedy Fantasy Romance
# 0 1 1 1 1 1 0
# 1 1 0 1 0 1 0
# 2 0 0 0 1 0 1
genres = movies_df['genres'].str.get_dummies('|')
# print(genres[["Action", "Adventure", "Drama", "Horror"]].head())
# (9742, 20) 9742部電影,20種體裁
# print(genres.values.shape)
# 將genres作為電影的輸入特征
movie_feat = torch.from_numpy(genres.values).to(torch.float)
assert movie_feat.size() == (9742,20)# 同理對評分進行處理
ratings_df = pd.read_csv(ratings_path)# 提取出每個用戶的id
"""
ratings_data = {'userId': [10, 20, 10, 30, 20, 40, 30, 50],'movieId': [101, 101, 102, 103, 104, 105, 106, 107],'rating': [3.5, 4.0, 2.5, 5.0, 4.0, 3.0, 4.5, 2.0]
}
"""
# unique_user_id = ([10, 20, 30, 40, 50])
unique_user_id = ratings_df['userId'].unique()
# 創建映射表
"""userId mappedID
0 10 0
1 20 1
2 30 2
3 40 3
4 50 4
"""
unique_user_id = pd.DataFrame(data={'userId': unique_user_id,'mappedID':pd.RangeIndex(len(unique_user_id))
})# 同理,對電影進行相同處理
unique_movie_id = ratings_df['movieId'].unique()
unique_movie_id = pd.DataFrame(data={'movieId':unique_movie_id,'mappedID':pd.RangeIndex(len(unique_movie_id))
})# 獲取user和movie的原始Id和映射ID
# 下方這代碼,不就是將評分表種的原始id與獲取的映射id進行映射而已嗎
ratings_user_id = pd.merge(ratings_df['userId'],unique_user_id,left_on='userId',right_on='userId',how='left')
ratings_user_id = torch.from_numpy(ratings_user_id['mappedID'].values)ratings_movie_id = pd.merge(ratings_df['movieId'], unique_movie_id,left_on='movieId', right_on='movieId', how='left')
ratings_movie_id = torch.from_numpy(ratings_movie_id['mappedID'].values)# 構造’edge_index'
# 在這里,你肯定會有這個疑惑?
# 為啥能那么剛好,例如用戶id為0的,剛好就是評論10號電影呢?
# 其實在一開始,所有的數據都是安排好的
# 'userId': [1, 2, 1, 3, 2, 4, 3, 5],
# 'movieId': [101, 101, 102, 103, 104, 105, 106, 107],
# 'rating': [3.5, 4.0, 2.5, 5.0, 4.0, 3.0, 4.5, 2.0]
# 是不是一一對應呢?只是將userid和movieid轉變為對應的mappedid而已
# 例如:userid:[0, 1, 0, 2, 1, 3, 2, 4]
# movieid:[0, 0, 1, 2, 3, 4, 5, 6]
edge_index_user_to_movie = torch.stack([ratings_user_id,ratings_movie_id],dim=0)
assert edge_index_user_to_movie.size() == (2,100836)
"""
tensor([[ 0, 0, 0, ..., 609, 609, 609],[ 0, 1, 2, ..., 3121, 1392, 2873]])
"""
# print(edge_index_user_to_movie)# 到現在,完成了數據的處理
# 初始化HeterData 對象。
data = HeteroData()# 保存節點索引
data['user'].node_id = torch.arange(len(unique_user_id))
data['movie'].node_id = torch.arange(len(movies_df))# 添加節點特征和邊索引
data['movie'].x = movie_feat # 電影的體裁作為節點特征,因為每個電影可能會有多個體裁
data['user','rates','movie'].edge_index =edge_index_user_to_movie# 添加反向邊,使得GNN能夠在兩個方向上傳遞消息,那不就是成為無向圖咯
data = T.ToUndirected()(data)print(data)
assert data.node_types == ["user", "movie"]
assert data.edge_types == [("user", "rates", "movie"),("movie", "rev_rates", "user")]assert data["user"].num_nodes == 610
assert data["user"].num_features == 0
assert data["movie"].num_nodes == 9742
assert data["movie"].num_features == 20assert data["user", "rates", "movie"].num_edges == 100836
assert data["movie", "rev_rates", "user"].num_edges == 100836