VGAE(Variational graph auto-encoders)論文及代碼解讀

一,論文來源

論文pdf

Variational graph auto-encoders

論文代碼

github代碼

二,論文解讀

理論部分參考:
Variational Graph Auto-Encoders(VGAE)理論參考和源碼解析
VGAE(Variational graph auto-encoders)論文詳解

簡要介紹: 本文是將變分自編碼器(Variational Auto-Encoders, VAE
)遷移到了圖領域,基本思路是:用已知的圖經過編碼(圖卷積)學到節點向量表示的分布,在分布中采樣得到節點的向量表示,然后進行解碼(鏈路預測)重新構建圖。
簡要介紹引用來自: 7. Variational Graph Auto-Encoders論文閱讀筆記

在這里插入圖片描述
細節方面感覺這個小姐姐講的非常好:
【GNN五大類 VGAE】(變分圖自編碼器):Variational Graph Auto-Encoders
理論方面其實前面幾位大佬說的很好了,這里我只是來闡述兩點:

1.測試方法:

模型在數據集的不完整版本上訓練,其中部分引用鏈接(邊)已被移除,而所有節點特征被保留。 我們從先前移除的邊和相同數量的隨機采樣的未連接節點對(非邊)形成驗證和測試集。我們根據模型正確分類邊緣和非邊緣的能力來比較模型。 驗證和測試集分別包含5%和10%的引用鏈接。 驗證集用于優化超參數。 我們對比了兩個流行的基線:譜聚類(SC) [5]和深度行走(DW) [6]。 SC和DW都提供節點嵌入z。 我們用Eq.4(左側)計算重構鄰接矩陣元素的得分。

2.熵

參考
交叉熵、相對熵(KL散度)、JS散度和Wasserstein距離(推土機距離)
正態分布N(mu,sigma)和N(0,1)之間的KL散度推導

三,代碼解讀

preprocessing.py

import numpy as np
import scipy.sparse as spdef sparse_to_tuple(sparse_mx):if not sp.isspmatrix_coo(sparse_mx):    #是否為csr_matrix類型sparse_mx = sparse_mx.tocoo()   #實現csc矩陣轉換為coo矩陣coords = np.vstack((sparse_mx.row, sparse_mx.col)).transpose()# np.vstack按垂直方向(行順序)堆疊數組構成一個新的數組,堆疊的數組需要具有相同的維度,transpose()作用是轉置values = sparse_mx.datashape = sparse_mx.shapereturn coords, values, shapedef preprocess_graph(adj):adj = sp.coo_matrix(adj)    #csr_matrix轉成coo_matrixadj_ = adj + sp.eye(adj.shape[0])   #S=A+I  #注意adj_的類型為csr_matrixrowsum = np.array(adj_.sum(1))    #rowsum的shape=(節點數,1),對于cora數據集來說就是(2078,1),sum(1)求每一行的和degree_mat_inv_sqrt = sp.diags(np.power(rowsum, -0.5).flatten())    #計算D^{-0.5}#p.diags:提取輸入矩陣(大小為m×n)的所有非零對角列。輸出的大小為 min(m,n)×p,其中p表示輸入矩陣的p個非零對角列#numpy.power():用于數組元素求n次方#flatten():返回一個折疊成一維的數組。adj_normalized = adj_.dot(degree_mat_inv_sqrt).transpose().dot(degree_mat_inv_sqrt).tocoo()# adj_.dot(degree_mat_inv_sqrt)得到 SD^{-0.5}# adj_.dot(degree_mat_inv_sqrt).transpose()得到(D^{-0.5})^{T}S^{T}=D^{-0.5}S,因為D和S都是對稱矩陣# adj_normalized即為D^{-0.5}SD^{-0.5}return sparse_to_tuple(adj_normalized)def mask_test_edges(adj):# Function to build test set with 10% positive links# NOTE: Splits are randomized and results might slightly deviate from reported numbers in the paper.# TODO: Clean up.# Remove diagonal elementsadj = adj - sp.dia_matrix((adj.diagonal()[np.newaxis, :], [0]), shape=adj.shape)adj.eliminate_zeros()# Check that diag is zero:assert np.diag(adj.todense()).sum() == 0#assert斷言是聲明其布爾值必須為真的判定,如果發生異常就說明表達示為假。#todense()方法將稀疏矩陣b轉換成稠密矩陣cadj_triu = sp.triu(adj) #取出稀疏矩陣的上三角部分的非零元素,返回的是coo_matrix類型adj_tuple = sparse_to_tuple(adj_triu)edges = adj_tuple[0]# 取除去節點自環的所有邊(注意,由于adj_tuple僅包含原始鄰接矩陣上三角的邊,所以edges中的邊雖然只記錄了邊<src,dis>,而不冗余記錄邊<dis,src>),shape=(邊數,2)每一行記錄一條邊的起始節點和終點節點的編號edges_all = sparse_to_tuple(adj)[0]# 取原始graph中的所有邊,shape=(邊數,2)每一行記錄一條邊的起始節點和終點節點的編號num_test = int(np.floor(edges.shape[0] / 10.))  #劃分測試集# np.floor返回數字的下舍整數num_val = int(np.floor(edges.shape[0] / 20.))   #劃分驗證集all_edge_idx = list(range(edges.shape[0]))np.random.shuffle(all_edge_idx) #打亂all_edge_idx的順序val_edge_idx = all_edge_idx[:num_val]   #劃分驗證集test_edge_idx = all_edge_idx[num_val:(num_val + num_test)]  #劃分測試集test_edges = edges[test_edge_idx]   #edges是除去節點自環的所有邊(因為數據集中的邊都是無向的,edges只是存儲了<src,dis>,沒有存儲<dis,src>,因為沒必要浪費內存),shape=(邊數,2)每一行記錄一條邊的起始節點和終點節點的編號val_edges = edges[val_edge_idx]train_edges = np.delete(edges, np.hstack([test_edge_idx, val_edge_idx]), axis=0)# np.vstack():在豎直方向上堆疊,np.hstack():在水平方向上平鋪。# np.hstack([test_edge_idx, val_edge_idx])將兩個list水平方向拼接成一維數組# np.delete的參數axis=0,表示刪除多行,刪除的行號由第一個參數確定def ismember(a, b, tol=5):rows_close = np.all(np.round(a - b[:, None], tol) == 0, axis=-1)# np.round返回浮點數x的四舍五入值,第二參數是保留的小數的位數# b[:, None]使b從shape=(邊數,2)變為shape=(邊數,1,2),而a是長度為2的list,a - b[:, None]觸發numpy的廣播機制# np.all()判斷給定軸向上的所有元素是否都為True,axis=-1(此時等同于axis=2)表示3維數組最里層的2維數組的每一行的元素是否都為Truereturn np.any(rows_close)# np.any()判斷給定軸向上是否有一個元素為True,現在不設置axis參數則是判斷所有元素中是否有一個True,有一個就返回True。# rows_close的shape=(邊數,1)# 至此,可以知道,ismember( )方法用于判斷隨機生成的<a,b>這條邊是否是已經真實存在的邊,如果是,則返回True,否則返回Falsetest_edges_false = []while len(test_edges_false) < len(test_edges):idx_i = np.random.randint(0, adj.shape[0])  #生成負樣本idx_j = np.random.randint(0, adj.shape[0])if idx_i == idx_j:continueif ismember([idx_i, idx_j], edges_all):continueif test_edges_false:if ismember([idx_j, idx_i], np.array(test_edges_false)):continueif ismember([idx_i, idx_j], np.array(test_edges_false)):continuetest_edges_false.append([idx_i, idx_j])val_edges_false = []while len(val_edges_false) < len(val_edges):idx_i = np.random.randint(0, adj.shape[0])idx_j = np.random.randint(0, adj.shape[0])if idx_i == idx_j:continueif ismember([idx_i, idx_j], train_edges):continueif ismember([idx_j, idx_i], train_edges):continueif ismember([idx_i, idx_j], val_edges):continueif ismember([idx_j, idx_i], val_edges):continueif val_edges_false:if ismember([idx_j, idx_i], np.array(val_edges_false)):continueif ismember([idx_i, idx_j], np.array(val_edges_false)):continueval_edges_false.append([idx_i, idx_j])assert ~ismember(test_edges_false, edges_all)   #assert(斷言)用于判斷一個表達式,在表達式條件為 false 的時候觸發異常。所以,這里是想要edges_all不含有test_edges_false,否則拋異常assert ~ismember(val_edges_false, edges_all)assert ~ismember(val_edges, train_edges)assert ~ismember(test_edges, train_edges)assert ~ismember(val_edges, test_edges)data = np.ones(train_edges.shape[0])# 重建出用于訓練階段的鄰接矩陣adj_train = sp.csr_matrix((data, (train_edges[:, 0], train_edges[:, 1])), shape=adj.shape)adj_train = adj_train + adj_train.T# #注意:這些邊列表只包含一個方向的邊(adj_train是矩陣,不是edge lists)return adj_train, train_edges, val_edges, val_edges_false, test_edges, test_edges_false

1.hstack()/vstack()分析
參考:Numpy中stack(),hstack(),vstack()函數詳解
np.vstack():在豎直方向上堆疊,np.hstack():在水平方向上平鋪。
np.hstack([test_edge_idx, val_edge_idx])將兩個list水平方向拼接成一維數組
2.preprocess_graph函數中格式轉換:
參考:scipy中稀疏矩陣coo_matrix, csr_matrix 的使用
3.b[:, None]維度擴充
這個我做了測試:

import numpy as npa = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])
b = a[:,None]
c = a[None,:]
d = a[:,:,None]
print(a.shape)
print(b.shape)
print(c.shape)
print(d.shape)

結果:
(4, 4)
(4, 1, 4)
(1, 4, 4)
(4, 4, 1)
可以發現None在哪,哪擴充了一維

model.py

import torch
import torch.nn as nn
import torch.nn.functional as F
import os
import numpy as npimport argsclass VGAE(nn.Module):def __init__(self, adj):super(VGAE,self).__init__()self.base_gcn = GraphConvSparse(args.input_dim, args.hidden1_dim, adj)self.gcn_mean = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)#lambda是匿名函數,冒號左邊是參數,多個參數用逗號隔開,右邊是表達式self.gcn_logstddev = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)def encode(self, X):hidden = self.base_gcn(X)self.mean = self.gcn_mean(hidden)self.logstd = self.gcn_logstddev(hidden)gaussian_noise = torch.randn(X.size(0), args.hidden2_dim)sampled_z = gaussian_noise*torch.exp(self.logstd) + self.mean# 這里使用torch.exp是因為論文中log(sigma)=GCN_{sigma}(X,A),torch.exp(self.logstd)即torch.exp(log(sigma))得到的是sigma;另外還有mu=GCN_{mu}(X,A).# 由于每個節點向量經過GCN后都有且僅有一個節點向量表示,所以呢,方差的對數log(sigma)和節點向量表示的均值mu分別是節點經過GCN_{sigma}(X,A)和GCN_{mu}(X,A)后得到的向量表示本身。# 從N(mu,sigma^2)中采樣一個樣本Z相當于在N(0,1)中采樣一個xi,然后Z = mu + xi×sigmareturn sampled_zdef forward(self, X):Z = self.encode(X)A_pred = dot_product_decode(Z)return A_predclass GraphConvSparse(nn.Module):def __init__(self, input_dim, output_dim, adj, activation = F.relu, **kwargs):super(GraphConvSparse, self).__init__(**kwargs)self.weight = glorot_init(input_dim, output_dim) self.adj = adjself.activation = activationdef forward(self, inputs):x = inputsx = torch.mm(x,self.weight)#torch.mm(a, b)是矩陣a和b矩陣相乘x = torch.mm(self.adj, x)outputs = self.activation(x)return outputsdef dot_product_decode(Z):A_pred = torch.sigmoid(torch.matmul(Z,Z.t()))return A_preddef glorot_init(input_dim, output_dim):init_range = np.sqrt(6.0/(input_dim + output_dim))initial = torch.rand(input_dim, output_dim)*2*init_range - init_rangereturn nn.Parameter(initial)class GAE(nn.Module):def __init__(self,adj):super(GAE,self).__init__()self.base_gcn = GraphConvSparse(args.input_dim, args.hidden1_dim, adj)self.gcn_mean = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)def encode(self, X):hidden = self.base_gcn(X)z = self.mean = self.gcn_mean(hidden)return zdef forward(self, X):Z = self.encode(X)A_pred = dot_product_decode(Z)return A_pred# class GraphConv(nn.Module):
# 	def __init__(self, input_dim, hidden_dim, output_dim):
# 		super(VGAE,self).__init__()
# 		self.base_gcn = GraphConvSparse(args.input_dim, args.hidden1_dim, adj)
# 		self.gcn_mean = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)
# 		self.gcn_logstddev = GraphConvSparse(args.hidden1_dim, args.hidden2_dim, adj, activation=lambda x:x)# 	def forward(self, X, A):
# 		out = A*X*self.w0
# 		out = F.relu(out)
# 		out = A*X*self.w0
# 		return out

1.lambda匿名函數
參考:python的匿名函數lambda解釋及用法
這里注意:匿名函數不需要return來返回值,表達式本身結果就是返回值。

train.py

import torch
import torch.nn.functional as F
from torch.optim import Adam
from sklearn.metrics import roc_auc_score, average_precision_score
import scipy.sparse as sp
import numpy as np
import os
import timefrom input_data import load_data
from preprocessing import *
import args
import model# Train on CPU (hide GPU) due to memory constraints
os.environ['CUDA_VISIBLE_DEVICES'] = ""adj, features = load_data(args.dataset)# 存儲原始鄰接矩陣(無對角線條目)以備后用
adj_orig = adj
adj_orig = adj_orig - sp.dia_matrix((adj_orig.diagonal()[np.newaxis, :], [0]), shape=adj_orig.shape)
# Scipy的dia_matrix函數見1.其中offsets數組中0表示對角線,-1表示對角線下面,正數表示對角線上面
# np.newaxis的作用是增加一個維度。[np.newaxis,:]是在np.newaxis這里增加1維。這樣改變維度的作用往往是將一維的數據轉變成一個矩陣
#diagonal()是獲得矩陣對角線
#adj_orig.diagonal()[np.newaxis, :], [0]代碼意思是先將對角線提取出來然后增加一維變為矩陣,方便后續計算
adj_orig.eliminate_zeros()
#eliminite_zeros() 存儲去掉0元素,返回的是稀疏存儲adj_train, train_edges, val_edges, val_edges_false, test_edges, test_edges_false = mask_test_edges(adj)
adj = adj_train #用于訓練的鄰接矩陣,類型為csr_matrix# Some preprocessing
adj_norm = preprocess_graph(adj)
#返回D^{-0.5}SD^{-0.5}的coords(坐標), data, shape,其中S=A+Inum_nodes = adj.shape[0]features = sparse_to_tuple(features.tocoo())
#features的類型原為lil_matrix,sparse_to_tuple返回features的coords, data, shape
num_features = features[2][1]
features_nonzero = features[1].shape[0]# Create Model
pos_weight = float(adj.shape[0] * adj.shape[0] - adj.sum()) / adj.sum()
#注意,adj的每個元素非1即0。pos_weight是用于訓練的鄰接矩陣中負樣本邊(既不存在的邊)和正樣本邊的倍數(即比值),這個數值在二分類交叉熵損失函數中用到,
#如果正樣本邊所占的比例和負樣本邊所占比例失衡,比如正樣本邊很多,負樣本邊很少,那么在求loss的時候可以提供weight參數,將正樣本邊的weight設置小一點,負樣本邊的weight設置大一點,
#此時能夠很好的平衡兩類在loss中的占比,任務效果可以得到進一步提升。參考:https://www.zhihu.com/question/383567632
norm = adj.shape[0] * adj.shape[0] / float((adj.shape[0] * adj.shape[0] - adj.sum()) * 2)adj_label = adj_train + sp.eye(adj_train.shape[0])  #adj_train是用于訓練的鄰接矩陣,類型為csr_matrix
adj_label = sparse_to_tuple(adj_label)adj_norm = torch.sparse.FloatTensor(torch.LongTensor(adj_norm[0].T),     #其中adj_norm是D^{-0.5}SD^{-0.5}的coords, data, shapetorch.FloatTensor(adj_norm[1]), torch.Size(adj_norm[2]))
adj_label = torch.sparse.FloatTensor(torch.LongTensor(adj_label[0].T), torch.FloatTensor(adj_label[1]), torch.Size(adj_label[2]))
features = torch.sparse.FloatTensor(torch.LongTensor(features[0].T), torch.FloatTensor(features[1]), torch.Size(features[2]))
#torch.sparse.FloatTensor見詳解weight_mask = adj_label.to_dense().view(-1) == 1
# view的參數-1 表示做自適應性調整,如果參數只有一個參數-1,則表示將Tensor變成一維張量。
weight_tensor = torch.ones(weight_mask.size(0)) 
weight_tensor[weight_mask] = pos_weight
#用于在binary_cross_entropy中設置正樣本邊的weight。負樣本邊的weight都為1,正樣本邊的weight都為pos_weight# init model and optimizer
model = getattr(model,args.model)(adj_norm)
#getattr() 函數用于返回一個對象屬性值。optimizer = Adam(model.parameters(), lr=args.learning_rate)def get_scores(edges_pos, edges_neg, adj_rec):def sigmoid(x):return 1 / (1 + np.exp(-x))# Predict on test set of edgespreds = []pos = []for e in edges_pos:# print(e)# print(adj_rec[e[0], e[1]])preds.append(sigmoid(adj_rec[e[0], e[1]].item()))#item()取出單元素張量的元素值并返回該值,保持原元素類型不變,從而能夠保留原來的精度。所以在求loss,以及accuracy rate的時候一般用item()pos.append(adj_orig[e[0], e[1]])preds_neg = []neg = []for e in edges_neg:preds_neg.append(sigmoid(adj_rec[e[0], e[1]].data))neg.append(adj_orig[e[0], e[1]])preds_all = np.hstack([preds, preds_neg])labels_all = np.hstack([np.ones(len(preds)), np.zeros(len(preds_neg))])roc_score = roc_auc_score(labels_all, preds_all)ap_score = average_precision_score(labels_all, preds_all)return roc_score, ap_scoredef get_acc(adj_rec, adj_label):labels_all = adj_label.to_dense().view(-1).long()   #long()將數字或字符串轉換為一個長整型preds_all = (adj_rec > 0.5).view(-1).long()accuracy = (preds_all == labels_all).sum().float() / labels_all.size(0)return accuracy# train model
for epoch in range(args.num_epoch):t = time.time()A_pred = model(features)    #得到的A_pred每個元素不再是非1即0optimizer.zero_grad()loss = log_lik = norm*F.binary_cross_entropy(A_pred.view(-1), adj_label.to_dense().view(-1), weight = weight_tensor)# binary_cross_entropy參考:https://www.zhihu.com/question/383567632if args.model == 'VGAE':kl_divergence = 0.5/ A_pred.size(0) * (1 + 2*model.logstd - model.mean**2 - torch.exp(model.logstd)**2).sum(1).mean()# kl_divergence就是正態分布的KL散度,即n個(0.5*(1+log(sigma^2)-mu^2-sigma^2))的和,n為圖中節點的數量,也就是這里的A_pred.size(0)# 2*model.logstd即為2*log(sigma),根據運算法則,log(sigma^2)=2*log(sigma);model.mean**2即為mu^2;torch.exp(model.logstd)**2即為sigma^2# 1+log(sigma^2)-mu^2-sigma^2# sum(1)表示矩陣每一行內元素求和loss -= kl_divergenceloss.backward()optimizer.step()train_acc = get_acc(A_pred,adj_label)val_roc, val_ap = get_scores(val_edges, val_edges_false, A_pred)print("Epoch:", '%04d' % (epoch + 1), "train_loss=", "{:.5f}".format(loss.item()),"train_acc=", "{:.5f}".format(train_acc), "val_roc=", "{:.5f}".format(val_roc),"val_ap=", "{:.5f}".format(val_ap),"time=", "{:.5f}".format(time.time() - t))test_roc, test_ap = get_scores(test_edges, test_edges_false, A_pred)
print("End of training!", "test_roc=", "{:.5f}".format(test_roc),"test_ap=", "{:.5f}".format(test_ap))

1.pos_weight = float(adj.shape[0] * adj.shape[0] - adj.sum()) / adj.sum()分析
參考:pytorch中binary_cross_entropy損失函數中weight參數是如何設置的?
2.torch.sparse.FloatTensor詳解
參考:tensor torch 構造_TORCH.SPARSE
3.np.newaxis函數
參考:np.newaxis作用
4.Scipy的dia_matrix函數
參考:Python scipy中的dia_matrix詳解
這里進行進一步的講解:
例如代碼:

from scipy.sparse import dia_matrix
import numpy as np
if __name__ == '__main__':data = np.array([[1,2,3,4],[4,2,3,8],[7,2,4,5]])offsets = np.array([0,-1,2])a = dia_matrix((data,offsets),shape=(4,4)).toarray()print(a)

在這里插入圖片描述
這樣排列是按照:offsets中第一個元素與data第一行元素對應,并且按照data中第一行第一個元素為起始點,對角線排列。同理:
在這里插入圖片描述
然后以0為對角線開始節點,向下記錄,按照shape的格式,得到最后結果:
[[1 0 4 0]
[4 2 0 5]
[0 2 3 0]
[0 0 3 4]]

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389271.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389271.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389271.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

IIS7設置

IIS 7.0和IIS 6.0相比改變很大誰都知道&#xff0c;而且在IIS 7.0中用VS2005來調試Web項目也不是什么新鮮的話題&#xff0c;但是我還是第一次運用這個東東&#xff0c;所以在此記下我的一些過程&#xff0c;希望能給更多的后來者帶了一點參考。其實我寫這篇文章時也參考了其他…

tableau大屏bi_Excel,Tableau,Power BI ...您應該使用什么?

tableau大屏biAfter publishing my previous article on data visualization with Power BI, I received quite a few questions about the abilities of Power BI as opposed to those of Tableau or Excel. Data, when used correctly, can turn into digital gold. So what …

python 可視化工具_最佳的python可視化工具

python 可視化工具Disclaimer: I work for Datapane免責聲明&#xff1a;我為Datapane工作 動機 (Motivation) There are amazing articles on data visualization on Medium every day. Although this comes at the cost of information overload, it shouldn’t prevent you …

網絡編程 socket介紹

Socket介紹 Socket是應用層與TCP/IP協議族通信的中間軟件抽象層&#xff0c;它是一組接口。在設計模式中&#xff0c;Socket其實就是一個門面模式&#xff0c;它把復雜的TCP/IP協議族隱藏在Socket接口后面&#xff0c;對用戶來說&#xff0c;一組簡單的接口就是全部。 Socket通…

猿課python 第三天

字典 字典是python中唯一的映射類型,字典對象是可變的&#xff0c;但是字典的鍵是不可變對象&#xff0c;字典中可以使用不同的鍵值字典功能> dict.clear()          -->清空字典 dict.keys()          -->獲取所有key dict.values()      …

在C#中使用代理的方式觸發事件

事件&#xff08;event&#xff09;是一個非常重要的概念&#xff0c;我們的程序時刻都在觸發和接收著各種事件&#xff1a;鼠標點擊事件&#xff0c;鍵盤事件&#xff0c;以及處理操作系統的各種事件。所謂事件就是由某個對象發出的消息。比如用戶按下了某個按鈕&#xff0c;某…

BP神經網絡反向傳播手動推導

BP神經網絡過程&#xff1a; 基本思想 BP算法是一個迭代算法&#xff0c;它的基本思想如下&#xff1a; 將訓練集數據輸入到神經網絡的輸入層&#xff0c;經過隱藏層&#xff0c;最后達到輸出層并輸出結果&#xff0c;這就是前向傳播過程。由于神經網絡的輸出結果與實際結果…

使用python和pandas進行同類群組分析

背景故事 (Backstory) I stumbled upon an interesting task while doing a data exercise for a company. It was about cohort analysis based on user activity data, I got really interested so thought of writing this post.在為公司進行數據練習時&#xff0c;我偶然發…

3.Contructor(構造器)模式—精讀《JavaScript 設計模式》Addy Osmani著

同系列友情鏈接: 1.設計模式之初體驗—精讀《JavaScript 設計模式》Addy Osmani著 2.設計模式的分類—精讀《JavaScript 設計模式》Addy Osmani著 Construct&#xff08;構造器&#xff09;模式 在經典的面向對象編程語言中&#xff0c;Construtor是一種在內存已分配給該對象的…

BZOJ 3653: 談笑風生(離線, 長鏈剖分, 后綴和)

題意 給你一顆有 \(n\) 個點并且以 \(1\) 為根的樹。共有 \(q\) 次詢問&#xff0c;每次詢問兩個參數 \(p, k\) 。詢問有多少對點 \((p, a, b)\) 滿足 \(p,a,b\) 為三個不同的點&#xff0c;\(p, a\) 都為 \(b\) 的祖先&#xff0c;且 \(p\) 到 \(a\) 的距離不能超過 \(k\) 。 …

搜索引擎優化學習原理_如何使用數據科學原理來改善您的搜索引擎優化工作

搜索引擎優化學習原理Search Engine Optimisation (SEO) is the discipline of using knowledge gained around how search engines work to build websites and publish content that can be found on search engines by the right people at the right time.搜索引擎優化(SEO…

Siamese網絡(孿生神經網絡)詳解

SiameseFCSiamese網絡&#xff08;孿生神經網絡&#xff09;本文參考文章&#xff1a;Siamese背景Siamese網絡解決的問題要解決什么問題&#xff1f;用了什么方法解決&#xff1f;應用的場景&#xff1a;Siamese的創新Siamese的理論Siamese的損失函數——Contrastive Loss損失函…

Dubbo 源碼分析 - 服務引用

1. 簡介 在上一篇文章中&#xff0c;我詳細的分析了服務導出的原理。本篇文章我們趁熱打鐵&#xff0c;繼續分析服務引用的原理。在 Dubbo 中&#xff0c;我們可以通過兩種方式引用遠程服務。第一種是使用服務直聯的方式引用服務&#xff0c;第二種方式是基于注冊中心進行引用。…

期權價格的上限和下限

期權按照買方權利性質分為&#xff1a;看漲期權和看跌期權 1、首先&#xff0c;看漲期權的上限和下限 看漲期權價格上限為其標的資產價格。 看漲期權是給予買方一個在未來買入標的資產的權利&#xff0c;如果該權利的價格高于標的資產的價格&#xff0c;那么投資者不如直接購買…

一件登錄facebook_我從Facebook的R教學中學到的6件事

一件登錄facebookBetween 2018 to 2019, I worked at Facebook as a data scientist — during that time I was involved in developing and teaching a class for R beginners. This was a two-day course that was taught about once a month to a group of roughly 15–20 …

SiameseFC超詳解

SiameseFC前言論文來源參考文章論文原理解讀首先要知道什么是SOT&#xff1f;&#xff08;Siamese要做什么&#xff09;SiameseFC要解決什么問題&#xff1f;SiameseFC用了什么方法解決&#xff1f;SiameseFC網絡效果如何&#xff1f;SiameseFC基本框架結構SiameseFC網絡結構Si…

Python全棧工程師(字符串/序列)

ParisGabriel Python 入門基礎字符串&#xff1a;str用來記錄文本信息字符串的表示方式&#xff1a;在非注釋中凡是用引號括起來的部分都是字符串‘’ 單引號“” 雙引號 三單引""" """ 三雙引有內容代表非空字符串否則是空字符串 區別&#xf…

跨庫數據表的運算

跨庫數據表的運算&#xff0c;一直都是一個說難不算太難&#xff0c;說簡單卻又不是很簡單的、總之是一個麻煩的事。大量的、散布在不同數據庫中的數據表們&#xff0c;明明感覺要把它們合并起來&#xff0c;再來個小小的計算&#xff0c;似乎也就那么回事……但真要做起來&…

FCN全卷積網絡隨筆

參考&#xff1a;四、全卷積網絡FCN詳細講解&#xff08;超級詳細哦&#xff09; 這篇文章已經寫的很好了&#xff0c;這里說兩個我考慮的點。 第一個就是&#xff1a;FCN在縮小成heat map&#xff0c;為什么要通過上采樣還原回原圖大小&#xff1f; 我覺得這個的原因是因為&a…