做網(wǎng)站程序員都要先做維護么數(shù)字營銷是干啥的
基礎原理講解
應用路徑
卷積網(wǎng)絡最經(jīng)典的就是CNN,其 可以提取圖片中的有效信息,而生活中存在大量拓撲結構的數(shù)據(jù)。圖卷積網(wǎng)絡主要特點就是在于其輸入數(shù)據(jù)是圖結構數(shù)據(jù),即 G ( V , E ) G(V,E) G(V,E),其中V是節(jié)點,E是邊,能有效提取拓撲結構中的有效信息,實現(xiàn)節(jié)點分類,邊預測等。
基礎原理
其核心公式是:
H ( l + 1 ) = σ ( D ~ ? 1 / 2 A ~ D ~ ? 1 / 2 H l W l ) H^{(l+1)}=\sigma(\tilde{D}^{-1/2}\tilde{A}\tilde{D}^{-1/2}H^{l}W^l) H(l+1)=σ(D~?1/2A~D~?1/2HlWl)
其中:
- σ \sigma σ 是非線性激活函數(shù)
- D ~ \tilde{D} D~是度矩陣, D ~ i i = ∑ j A ~ i j \tilde{D}_{ii}=\sum_j\tilde{A}_{ij} D~ii?=∑j?A~ij?
- A ~ \tilde{A} A~是加了自環(huán)的鄰接矩陣,通常表示為 A + I A+I A+I, A A A是原始鄰接矩陣, I I I是單位矩陣
- H l H^l Hl是第 l l l層的節(jié)點特征矩陣, H l + 1 H^{l+1} Hl+1是第 l + 1 l+1 l+1層的節(jié)點特征矩陣
- W l W^l Wl是第 l l l層的學習權重矩陣
步驟講解:
1、鄰接矩陣歸一化: 將鄰接矩陣歸一化,使得鄰居節(jié)點特征對中心節(jié)點特征的貢獻相等。
2、特征聚合: 通過鄰接矩陣與節(jié)點特征矩陣相乘,實現(xiàn)鄰居特征聚合。
3、線性變換: 通過可學習的權重矩陣對聚合后的特征進行線性變換。
加自環(huán)的鄰接矩陣
A ~ = A + λ I \tilde{A} = A+\lambda I A~=A+λI
鄰接矩陣加上一個單位矩陣, λ \lambda λ是一個可以訓練的參數(shù),但也可直接取1。加自環(huán) 是為了增強節(jié)點自我特征表示,這樣在進行圖卷積操作時,節(jié)點不僅會聚合來自鄰居節(jié)點的特征,還會聚合自己的特征。
圖卷積操作
圖片的卷積是一個一個卷積核,在圖片上滑動著做卷積。圖的卷積就是自己加鄰居一起做加和。
即:
A ~ X \tilde{A}X A~X
度矩陣求解
D ~ i i = ∑ j A ~ i j \tilde{D}_{ii}=\sum_j\tilde{A}_{ij} D~ii?=j∑?A~ij?
標準化
在進行加和時,節(jié)點的度不同,有存在較高度值的節(jié)點和較低度值的節(jié)點,這可能導致梯度爆炸或梯度消失的問題。
根據(jù)度矩陣,求逆,然后 D ~ ? 1 A ~ D ~ ? 1 X \tilde{D}^{-1}\tilde{A} \tilde{D}^{-1}X D~?1A~D~?1X,就進行了標準化,前一個 D ~ ? 1 \tilde{D}^{-1} D~?1是對行進行標準化,后一個 D ~ ? 1 \tilde{D}^{-1} D~?1是對列進行標準化。能夠實現(xiàn)給與低度節(jié)點更大的權重,從而降低高節(jié)點的影響。
在上式推導中, D ~ ? 1 A ~ D ~ ? 1 X \tilde{D}^{-1}\tilde{A} \tilde{D}^{-1}X D~?1A~D~?1X 做了兩次標準化,所以修改上式為 D ~ ? 1 / 2 A ~ D ~ ? 1 / 2 X \tilde{D}^{-1/2}\tilde{A} \tilde{D}^{-1/2}X D~?1/2A~D~?1/2X
簡單python實現(xiàn)
基于cora數(shù)據(jù)集實現(xiàn)節(jié)點分類
- cora數(shù)據(jù)集處理
# cora數(shù)據(jù)集測試
raw_data = pd.read_csv('./data/data/cora/cora.content', sep='\t', header=None)
print("content shape: ", raw_data.shape)raw_data_cites = pd.read_csv('./data/data/cora/cora.cites', sep='\t', header=None)
print("cites shape: ", raw_data_cites.shape)features = raw_data.iloc[:,1:-1]
print("features shape: ", features.shape)# one-hot encoding
labels = pd.get_dummies(raw_data[1434])
print("\n----head(3) one-hot label----")
print(labels.head(3))
l_ = np.array([0,1,2,3,4,5,6])
lab = []
for i in range(labels.shape[0]):lab.append(l_[labels.loc[i,:].values.astype(bool)][0])
#構建鄰接矩陣
num_nodes = raw_data.shape[0]# 將節(jié)點重新編號為[0, 2707]
new_id = list(raw_data.index)
id = list(raw_data[0])
c = zip(id, new_id)
map = dict(c)# 根據(jù)節(jié)點個數(shù)定義矩陣維度
matrix = np.zeros((num_nodes,num_nodes))# 根據(jù)邊構建矩陣
for i ,j in zip(raw_data_cites[0],raw_data_cites[1]):x = map[i] ; y = map[j]matrix[x][y] = matrix[y][x] = 1 # 無向圖:有引用關系的樣本點之間取1# 查看鄰接矩陣的元素
print(matrix.shape)
- GCN網(wǎng)絡實現(xiàn)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f'Using device: {device}')
class GCNLayer(nn.Module):def __init__(self, in_features, out_features):super(GCNLayer, self).__init__()self.linear = nn.Linear(in_features, out_features)def forward(self, x, adj):rowsum = torch.sum(adj,dim=1)d_inv_sqrt = torch.pow(rowsum,-0.5)d_inv_sqrt[torch.isinf(d_inv_sqrt)] =0.0d_mat_inv_sqrt = torch.diag(d_inv_sqrt)adj_normalized = torch.mm(torch.mm(d_mat_inv_sqrt,adj),d_mat_inv_sqrt)out = torch.mm(adj_normalized,x)out = self.linear(out)return out
class GCN(nn.Module):def __init__(self, n_features, n_hidden, n_classes):super(GCN, self).__init__()self.gcn1 = GCNLayer(n_features, n_hidden)self.gcn2 = GCNLayer(n_hidden, n_classes)def forward(self, x, adj):x = self.gcn1(x, adj)x = F.relu(x)x = self.gcn2(x, adj)return x#F.log_softmax(x, dim=1)
# 示例數(shù)據(jù)(實際數(shù)據(jù)應根據(jù)具體情況加載)features = torch.tensor(features.values, dtype=torch.float32)
adj = torch.tensor(matrix, dtype=torch.float32)
labels = torch.tensor(lab, dtype=torch.long)
# features = torch.tensor([[1, 0], [0, 1], [1, 1]], dtype=torch.float32)
# adj = torch.tensor([[1, 1, 0], [1, 1, 1], [0, 1, 1]], dtype=torch.float32)
# labels = torch.tensor([0, 1, 0], dtype=torch.long)# 模型參數(shù)
n_features = features.shape[1]
n_hidden = 16
n_classes = len(torch.unique(labels))# 創(chuàng)建模型
model = GCN(n_features, n_hidden, n_classes)
model = model.cuda()
optimizer = optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.CrossEntropyLoss()
# 訓練模型
n_epochs = 200
for epoch in range(n_epochs):model.train()features, labels = features.cuda(), labels.cuda()adj = adj.cuda()optimizer.zero_grad()output = model(features, adj)loss = loss_fn(output, labels)loss.backward()optimizer.step()if (epoch + 1) % 20 == 0:print(f'Epoch {epoch+1}, Loss: {loss.item()}')
print("Training complete.")
參考
cora數(shù)據(jù)集及簡介
圖卷積網(wǎng)絡詳細介紹
GCN講解