图神经网络构建稀疏矩阵

构建稀疏矩阵

  • 构建步骤
    • 构建邻接矩阵
    • 生成归一化邻接矩阵(考虑自身影响)
    • 生成归一化邻接矩阵(不考虑自身影响)
    • 稀疏矩阵(Sparse Matrix)转换为 PyTorch 稀疏张量(Sparse Tensor)
  • 代码实现
    • 构建数据
    • 构建邻接矩阵
    • 生成归一化邻接矩阵(考虑自身影响)
    • 生成归一化邻接矩阵(不考虑自身影响)
    • 稀疏矩阵(Sparse Matrix)转换为 PyTorch 稀疏张量(Sparse Tensor)

以用户-物品为例,构建二部图 : 假设现有5个用户,10个物品

构建步骤

构建邻接矩阵

1、创建大小为【(用户数量+物品数量) * (用户数量+物品数量) 】的空稀疏矩阵A(邻接矩阵)
2、创建【用户数量 * 物品数量】的空稀疏矩阵B
3、在稀疏矩阵B中,将用户访问过的物品的位置设为1.
3、将稀疏矩阵A、B转换为 LIL 格式,LIL 是 SciPy 中的一种稀疏矩阵存储格式,它以两个列表表示稀疏矩阵的非零元素:一个存储每一行的非零元素及其对应的列索引,另一个存储每一行的长度。
4、将B赋值给A,稀疏矩阵A,如图所示:

在这里插入图片描述
5、将得到的 LIL 格式的稀疏矩阵再转回为 DOK 格式的稀疏矩阵

生成归一化邻接矩阵(考虑自身影响)

1、将邻接矩阵和同样大小的单位矩阵相加得到新的邻接矩阵
2、计算邻接矩阵每行的和,得到一个包含每行和的 NumPy 数组。
3、计算每行和的倒数
4、将无穷大的元素(原本是分母为0的情况)设置为0。这是为了避免除以0的情况。
5、创建一个对角矩阵,对角线上的元素为 d_inv 数组中的元素。这个对角矩阵实际上是d_inv的逆矩阵。
6、计算归一化邻接矩阵,即 D^-1 * A。
7、将结果矩阵转换为 COO 格式(Coordinate format,坐标格式)

生成归一化邻接矩阵(不考虑自身影响)

1、计算邻接矩阵每行的和,得到一个包含每行和的 NumPy 数组。
2、计算每行和的倒数
3、将无穷大的元素(原本是分母为0的情况)设置为0。这是为了避免除以0的情况。
4、创建一个对角矩阵,对角线上的元素为 d_inv 数组中的元素。这个对角矩阵实际上是d_inv的逆矩阵。
5、计算归一化邻接矩阵,即 D^-1 * A。
6、将结果矩阵转换为 COO 格式(Coordinate format,坐标格式)

稀疏矩阵(Sparse Matrix)转换为 PyTorch 稀疏张量(Sparse Tensor)

1、创建一个包含两个数组coo.row 和 coo.col 的张量
2、 创建一个稀疏张量。

代码实现

背景描述:
商品又:商品1: 电子产品 - 智能手机;商品2: 家居用品 - 双人床;商品3: 服装配饰 - 运动鞋;商品4: 图书 - 小说;商品5: 食品 - 咖啡豆;商品6: 家电 - 液晶电视;商品7: 美妆 - 口红;商品8: 运动户外 - 自行车;商品9: 宠物用品 - 猫粮;商品10: 儿童玩具 - 拼图玩具
Alice 购买了 智能手机 (商品1)
Bob 购买了 双人床 (商品2)
Charlie 购买了 运动鞋 (商品3)
David 购买了 小说 (商品4)
Emily 购买了 咖啡豆 (商品5)

构建数据

import pandas as pd

# 用户名数据
user_data = {
    'User_ID': [1, 2, 3, 4, 5],
    'Username': ['Alice', 'Bob', 'Charlie', 'David', 'Emily']
}

# 商品数据
product_data = {
    'Product_ID': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    'Category': ['电子产品', '家居用品', '服装配饰', '图书', '食品', '家电', '美妆', '运动户外', '宠物用品', '儿童玩具'],
    'Product_Name': ['智能手机', '双人床', '运动鞋', '小说', '咖啡豆', '液晶电视', '口红', '自行车', '猫粮', '拼图玩具']
}

# 购买关系数据
purchase_data = {
    'User_ID': [1, 2, 3, 4, 5],
    'Product_ID': [1, 2, 3, 4, 5]
}

# 创建DataFrame
users_df = pd.DataFrame(user_data)
products_df = pd.DataFrame(product_data)
purchases_df = pd.DataFrame(purchase_data)

# 将User_ID和Product_ID用作索引,以便连接DataFrame
users_df.set_index('User_ID', inplace=True)
products_df.set_index('Product_ID', inplace=True)

# 连接DataFrame,形成购买关系DataFrame
purchase_df = pd.merge(purchases_df, users_df, how='left', left_on='User_ID', right_index=True)
purchase_df = pd.merge(purchase_df, products_df, how='left', left_on='Product_ID', right_index=True)

# 显示结果
print(purchase_df)

构建邻接矩阵

import scipy.sparse as sp
import numpy as np
n_users = len(set(purchase_df["User_ID"]))
n_items = len(set(purchase_df["Product_ID"]))

adj_A = sp.dok_matrix((n_users + n_items, n_users + n_items), dtype=np.float32)
adj_B = sp.dok_matrix((n_users, n_items), dtype=np.float32)

for user in set(purchase_df["User_ID"]):
    for item in set(purchase_df[purchase_df["User_ID"] == user]["Product_ID"]):
        adj_B[user - 1, item - 1] = 1.   # 减1是因为物品和用户标号从1开始

adj_B = adj_B.tolil()
adj_A = adj_A.tolil()

# # 查看adj_B的值
# for i in range(adj_B.shape[0]):
#     row_values = adj_B.data[i]
#     row_indices = adj_B.rows[i]
    
#     for col_index, value in zip(row_indices, row_values):
#         print(f"Value at ({i}, {col_index}): {value}")

# # 查看adj_B.T的值
# for i in range(adj_B.T.shape[0]):
#     row_values = adj_B.T.data[i]
#     row_indices = adj_B.T.rows[i]
    
#     for col_index, value in zip(row_indices, row_values):
#         print(f"Value at ({i}, {col_index}): {value}")

adj_A[:n_users, n_users:] = adj_B
adj_A[n_users:, :n_users] = adj_B.T
adj_A= adj_A.todok()

# # 查看adj_A的值
# for (row, col), value in adj_A.items():
#     print(f"Value at ({row}, {col}): {value}")

生成归一化邻接矩阵(考虑自身影响)

adj_mat= adj_A+ sp.eye(adj_A.shape[0])
# #--- 查看adj_mat的数据
# # coo_mat = adj_mat.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# ----------------------------------------------
row_sum = np.array(adj_mat.sum(1))   # 每行和
d_inv = np.power(row_sum, -1).flatten()  # 和倒数
d_inv[np.isinf(d_inv)] = 0.
d_mat_inv = sp.diags(d_inv)   # 对角矩阵
# #--- 查看d_mat_inv的数据
# # coo_mat = d_mat_inv.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# # ----------------------------------------------------
norm_adj = d_mat_inv.dot(adj_mat)
# #--- 查看norm_adj的数据
# # coo_mat = norm_adj.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# --------------------------------------------------------
norm_adj = norm_adj.tocoo()

生成归一化邻接矩阵(不考虑自身影响)

adj_mat= adj_A
# #--- 查看adj_mat的数据
# # coo_mat = adj_mat.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# ----------------------------------------------
row_sum = np.array(adj_mat.sum(1))   # 每行和
d_inv = np.power(row_sum, -1).flatten()  # 和倒数
d_inv[np.isinf(d_inv)] = 0.
d_mat_inv = sp.diags(d_inv)   # 对角矩阵
# #--- 查看d_mat_inv的数据
# # coo_mat = d_mat_inv.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# # ----------------------------------------------------
norm_adj = d_mat_inv.dot(adj_mat)
# #--- 查看norm_adj的数据
# # coo_mat = norm_adj.tocoo()
# # for i, j, value in zip(coo_mat.row, coo_mat.col, coo_mat.data):
# #     print(f"Value at ({i}, {j}): {value}")
# --------------------------------------------------------
norm_adj = norm_adj.tocoo()

稀疏矩阵(Sparse Matrix)转换为 PyTorch 稀疏张量(Sparse Tensor)

i = torch.LongTensor([norm_adj.row, norm_adj.col])
v = torch.from_numpy(norm_adj.data).float()
sparse_norm_adj = torch.sparse.FloatTensor(i, v, norm_adj.shape)
# tensor(indices=tensor([[0, 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6,
#                         6, 7, 7, 8, 8, 9, 9, 9, 9],
#                        [9, 5, 0, 6, 1, 7, 6, 5, 2, 9, 8, 3, 9, 4, 5, 2, 0, 6, 2,
#                         1, 7, 2, 8, 3, 9, 4, 3, 0]]),
#        values=tensor([0.3333, 0.3333, 0.3333, 0.5000, 0.5000, 0.2500, 0.2500,
#                       0.2500, 0.2500, 0.3333, 0.3333, 0.3333, 0.5000, 0.5000,
#                       0.3333, 0.3333, 0.3333, 0.3333, 0.3333, 0.3333, 0.5000,
#                       0.5000, 0.5000, 0.5000, 0.2500, 0.2500, 0.2500, 0.2500]),
#        size=(10, 10), nnz=28, layout=torch.sparse_coo)

Tips:
如有问题,麻烦指正,谢谢各位!
如有疑问,请留言,谢谢!
持续更新…