博客內容將首發在微信公衆號"跟我一起讀論文啦啦",上面會定期分享機器學習、深度學習、數據挖掘、自然語言處理等高質量論文,歡迎關注!
本次要分享的論文是來自KDD2016的工作,論文鏈接Structural Deep Network Embedding,(簡稱SDNE)參考的代碼鏈接 CODE。本篇論文第一次利用深度學習知識做圖表示學習,其中也定義了與 LINE 中的一階相似性和二階相似性類似的概念,使其能夠捕捉到圖中高階的非線性網絡結構,同時能保留局部和全局結構信息,並且對稀疏網絡具有較好的魯棒性。與 LINE 論文方法相比,該論文方法在一些細節較容易讓人理解。其實驗部分證明了本方法與當時其他Graph Embedding方法相比,具有明顯的優越性。
論文動機和創新點
-
現實世界中,圖網絡結構無處不在,例如在推薦系統,社羣團體挖掘等應用上,如何挖掘圖中的信息變得尤爲重要。其中一個重要的基礎問題就是,如何學習到有用的network embedding,通常的做法是將embedding空間映射到較低維的空間中進行節點的表示學習。
-
network embedding的學習面臨以下三個挑戰:
1、如何學習到圖結構背後的高級非線性信息,是一個比較困難的事情。
2、學習到的network embedding需要能保存 network structure信息,然而圖的結構信息是非常複雜的,如何學習到節點的局部和全局結構信息是比較困難的。
3、現實世界中,大部分圖網絡是稀疏的,如果僅僅利用由有邊相連的部分進行學習,效果是很差的,所以學習到的network embedding能夠對稀疏網絡圖具有魯棒性。 -
上面提到的三個挑戰,淺層模型很難有效解決,基於此,本文提出了 Structural Deep Network Embedding,即SDNE。更具體地說,
1、利用深度神經網絡,將數據映射到一個高度非線性的潛在空間以保存網絡結構和捕捉高級的非線性特徵信息,並且對稀疏網絡具有魯棒性。
2、提出了一個新的半監督結構的深度模型,同時優化了一階和二階相似度,使得所學習的表示保留了局部和全局網絡結構,並且對稀疏網絡具有魯棒性。
問題描述
圖結構定義
上述公式中 表示 節點集合, 表示邊集合,對每天邊上的權重 , 爲鄰接矩陣;對於有權重圖, 表示節點 和節點 的關係強弱;若無連接,則,反之 ,若爲無權重圖,。如上所描述的圖G基本上可以囊括現實世界中的信息網絡。
一階相似度
對於圖中任意兩個節點 都可以由邊進行連接,如果在圖中兩節點有邊則 ,否則等於0,這種定義也是符合現實邏輯的,在information network中,如果兩個用戶存在連接關係,則該兩個用戶的性格、興趣等可能存在相似性、如果兩個網頁存在連向彼此的鏈接,則該網頁內容可能存在相似性等等。本文中一階相似度用來保存全局圖結構信息。
二階相似度
顯然一階相似度有其侷限性,僅僅利用了有邊連接的部分進行學習,而現實世界中圖大部分是稀疏的;二階相似度指的是 圖中兩節點 相鄰節點的相似程度。本文中二階相似度用來保存局部圖結構信息。
SDNE模型
損失函數
二階相似度優化目標
可以查看上面SDNE網絡圖,圖中深度自編碼由多層神經網絡組成:
其中,自編碼輸入爲,即爲鄰接矩陣的第 行數據。
自編碼損失函數:
爲了避免稀疏問題( 中可能存在大量0),這裏加上一個懲罰項 ,對非0元素給予更大的懲罰:
顯然以上這種重構過程,將使具有相似鄰域結構的頂點具有相似的潛在表示,可以保存全局的網絡結構信息。
一階相似度優化目標
除了捕捉和保存圖的全局結構信息,還需要捕捉保存圖的局部結構信息。
其中 表示節點和節點 的權重, 表示節點 在自編碼的第 層的降維輸出,上式借鑑了 Laplacian Eigenmaps的思想,目的讓相似的頂點在降維後的低維空間裏仍舊儘量接近。
通過線性代數的知識,可將上式轉化爲如下:
其中 爲圖的拉普拉斯矩陣,D爲圖的度矩陣,S爲鄰接矩陣。
SDNE損失函數
其中:
整體算法流程
實驗
本論文實驗部分,利用了五個公開的數據集,在三類任務上進行了實驗,與其baseline model相比,sdne方法無論在泛化能力上、對稀疏網絡的魯棒性上,可視化上等都有較大的優越性。
代碼
代碼參考自:keras-sdne
一階、二階損失函數
def l_2nd(beta):
def loss_2nd(y_true, y_pred):
b_ = np.ones_like(y_true)
b_[y_true != 0] = beta
x = K.square((y_true - y_pred) * b_)
t = K.sum(x, axis=-1, )
return K.mean(t)
return loss_2nd
def l_1st(alpha):
def loss_1st(y_true, y_pred):
L = y_true
Y = y_pred
batch_size = tf.to_float(K.shape(L)[0])
return alpha * 2 * tf.linalg.trace(tf.matmul(tf.matmul(Y, L, transpose_a=True), Y)) / batch_size
return loss_1st
創建模型
def create_model(node_size, hidden_size=[256, 128], l1=1e-5, l2=1e-4):
A = Input(shape=(node_size,))
L = Input(shape=(None,))
fc = A
for i in range(len(hidden_size)):
if i == len(hidden_size) - 1:
fc = Dense(hidden_size[i], activation='relu',
kernel_regularizer=l1_l2(l1, l2), name='1st')(fc)
else:
fc = Dense(hidden_size[i], activation='relu',
kernel_regularizer=l1_l2(l1, l2))(fc)
Y = fc
for i in reversed(range(len(hidden_size) - 1)):
fc = Dense(hidden_size[i], activation='relu',
kernel_regularizer=l1_l2(l1, l2))(fc)
A_ = Dense(node_size, 'relu', name='2nd')(fc)
## A->A_ 自編碼
## L->y Laplacian Eigenmaps
model = Model(inputs=[A, L], outputs=[A_, Y])
emb = Model(inputs=A, outputs=Y)
return model, emb
模型訓練
def train(self, batch_size=1024, epochs=1, initial_epoch=0, verbose=1):
if batch_size >= self.node_size:
if batch_size > self.node_size:
print('batch_size({0}) > node_size({1}),set batch_size = {1}'.format(
batch_size, self.node_size))
batch_size = self.node_size
return self.model.fit([self.A.todense(), self.L.todense()], [self.A.todense(), self.L.todense()],
batch_size=batch_size, epochs=epochs, initial_epoch=initial_epoch, verbose=verbose,
shuffle=False, )
else:
steps_per_epoch = (self.node_size - 1) // batch_size + 1
hist = History()
hist.on_train_begin()
logs = {}
for epoch in range(initial_epoch, epochs):
start_time = time.time()
losses = np.zeros(3)
for i in range(steps_per_epoch):
index = np.arange(
i * batch_size, min((i + 1) * batch_size, self.node_size))
A_train = self.A[index, :].todense()
L_mat_train = self.L[index][:, index].todense()
inp = [A_train, L_mat_train]
## inp 作爲模型的輸入;同時又做模型的y_true
batch_losses = self.model.train_on_batch(inp, inp)
losses += batch_losses
losses = losses / steps_per_epoch
個人總結
- 本論文方法中的 一階二階相似度的計算方式,比起LINE來說,直觀上較好理解。