!!!この記事には実装は含まれていません。公式コードからの出力処理の考え方を理解するだけです。機会があれば実装し、公式コードのアドレスと以前に書いた論文の解釈を公開します。いつものように:
SGRN ネットワーク github プロジェクト アドレスhttps://github.com/simblah/SGRN_torch [グラフ ニューラル ネットワーク] 空間関係知覚リレーショナル ネットワーク (SGRN) - 論文解釈https://blog.csdn.net/weixin_37878740/article/details/129837774? spm=1001.2014.3001.5501
1. ネットワーク枠組みの見直し
論文で言及されている SGRN ネットワークは、Faster R-CNN にリレーショナル グラフ学習器と空間認識推論モジュールを埋め込んでおり、RoI Pooling をRoI Alignに変更して いることがはっきりとわかります。そのため、これらの部分のコードに焦点を当てます。
2、コード解釈
解釈されたコードはプロジェクト内にあります: lib->nets->network_gcn。順方向伝達関数の構造は次のとおりです。
def forward(self, image, im_info, gt_boxes=None, mode='TRAIN'):
#.....
rois, cls_prob, bbox_pred = self._predict()
#....
順伝達関数の残りのコードはデータの前処理と損失関数の計算であり、次に、rois、cls_prob、bbox_pred はすべて self._predict() 関数によって計算されます。したがって、_predict () 関数にジャンプします。
# This is just _build_network in tf-faster-rcnn
net_conv = self._image_to_head() #骨干网络提取
# build the anchors for the image
self._anchor_component(net_conv.size(2), net_conv.size(3))
rois = self._region_proposal(net_conv) #获取候选区域
if cfg.POOLING_MODE == 'align': #兴趣域池化
pool5 = self._roi_align_layer(net_conv, rois)
else:
pool5 = self._roi_pool_layer(net_conv, rois)
fc7 = self._head_to_tail(pool5)
cls_prob, bbox_pred = self._region_classification(fc7)
これは元々 Faster R-CNN に属していたコードで、図のこの部分に対応する RoI Pooling が変更されています。
1.ROIの調整
def _roi_align_layer(self, bottom, rois):
return RoIAlign((cfg.POOLING_SIZE, cfg.POOLING_SIZE), 1.0 / 16.0,0)(bottom, rois)
def _roi_pool_layer(self, bottom, rois):
return RoIPool((cfg.POOLING_SIZE, cfg.POOLING_SIZE),1.0 / 16.0)(bottom, rois)
RoI Pool と比較して、RoI Align には最後にもう 1 つのパラメータがあり、これは双線形補間におけるサンプリング ポイントの数を制御するために使用されます。デフォルト値は -1 で、コード内では 0 に設定されます。
2. 関係学習者
図のこの部分に対応する、重みからグラフ (隣接行列) を構築するために使用されます。
num_rois = rois.shape[0] #通道数
z = self.relation_fc_1(fc7)
z = F.relu(self.relation_fc_2(z))
eps = torch.mm(z, z.t())
_, indices = torch.topk(eps, k=32, dim=0)
このうち、relation_fc_1()とrelationship_fc_2()は両方とも全結合層です。2 つの全結合層は重ね合わされ、その後にrelu活性化関数が続きます。fc7 (つまり、推奨領域) がモジュールによって処理された後、シーケンス z長さ 256 のものが得られます。
self.relation_fc_1 = nn.Linear(self._fc7_channels, 256)
self.relation_fc_2 = nn.Linear(256, 256)
次に、torch.mm( ) <目的は2 つの行列を乗算することです> の後、 z とその転置を乗算して、隣接行列 epsを取得します。
隣接行列 eps はtorch.topk() を通過します。 torch.topk の機能は、並べ替えのためにシーケンス内の最初の k 要素を見つけることです。戻り値は 2 つあります: 最初の値は並べ替えられた配列、2 番目の値は配列内元の配列内の取得された要素の位置ラベル。この関数の目的は、スパース グラフ(元のテキストに対応して最大 3 つの値を取る)を構築することです。
並べ替えルールは次のとおりです: 列内の最大のデータなど:
tensor1=torch.tensor([ [9,1,2,1,9,1],
[3,4,5,1,1,1],
[7,8,9,1,1,1],
[1,4,7,1,1,2]])
values,indices=torch.topk(tensor1, k=3, dim=0)
print(values)
print(indices)
実験の結果、各行の最大の k データが前に記載されていることがわかります。
tensor([[9, 8, 9, 1, 9, 2],
[7, 4, 7, 1, 1, 1],
[3, 4, 5, 1, 1, 1]])
tensor([[0, 2, 2, 0, 0, 3],
[2, 1, 3, 1, 1, 0],
[1, 3, 1, 2, 2, 1]])
3. 空間認識推論モジュール
①埋め込まれた埋め込みベクトルを取得する
これは、図のこの部分に対応する、グラフ内の特徴をグラフ (隣接行列) に埋め込むために使用されます。
具体的な構造は以下の通りで、3つの枝(それぞれ接続関係、カテゴリ関係、位置関係を表すグラフ/隣接行列、分類重み、予測ボックス)から構成されます。
cls_w = self.cls_score_net.weight
represent = torch.mm(cls_prob, cls_w)
cls_prob (分類器から導出された型予測重み) と cls_w (fc7 が線形分類器を通過する)を乗算します。
self.cls_score_net = nn.Linear(self._fc7_channels, self._num_classes)
②距離関数を取得する
cls_pred = torch.max(cls_prob, 1)[1]
bbox_pred_reshape = bbox_pred.view(-1, 1001, 4)
bbox_pred_cls = torch.zeros(num_rois, 4)
for i, cls in enumerate(cls_pred):
bbox_pred_cls[i] = bbox_pred_reshape[i][cls]
bbox_pred_ctr = bbox_pred_cls[:, 0:2] + bbox_pred_cls[:, 2:4]
torch.max()[1] は最大値のインデックスを返します。実験は次のとおりです。
x = torch.max(tensor1,1)[1] #tensor1同上例
print(x)
tensor([0, 2, 2, 2]) #得到每一列最大值的索引
bbox_pred は、fc7 を関係分類器で処理した後に得られるターゲット ボックス予測であり、view を使用して再構成し、cls_pred を走査し、各列の最大の要素 (4 つの座標 ) を取り出し、次の式に従って計算します。
、
relation = torch.empty(2, 32 * num_rois, dtype=torch.long).to(self._device)
# U = torch.empty(32*128, 2).to(self._device)
relation[0] = torch.Tensor(list(range(num_rois)) * 32) # , type=torch.long)
relation[1] = indices.view(-1)
coord_i = bbox_pred_ctr[relation[0]]
coord_j = bbox_pred_ctr[relation[1]]
d = torch.sqrt((coord_i[:, 0] - coord_j[:, 0]) ** 2 + (coord_i[:, 1] - coord_j[:, 1]) ** 2)
#距离
theta = torch.atan2((coord_j[:, 1] - coord_i[:, 1]), (coord_j[:, 0] - coord_i[:, 0]))
#角度
U = torch.stack([d, theta], dim=1).to(self._device)
#位置嵌入
③グラフの畳み込み
グラフ畳み込みの定義は、パラメータが次のとおりであることです: 入力グラフ サイズ、出力グラフ サイズ、深さ、畳み込みカーネル サイズ
self.gaussian = GMMConv(self._fc7_channels, self._fc7_channels, dim=2, kernel_size = 25)
このグラフ畳み込み処理を使用します: グラフ/隣接行列、分類重み、予測ボックス(実際の 3 つの入力は次のとおりです: グラフ ( graph )、特徴 ( feat )、擬似座標 ( pseudo )、特徴/特徴のサイズは ( )<それぞれ: ノードの数、入力フィーチャのサイズ>;擬似座標/擬似のサイズは ( )<それぞれ: エッジの数、擬似座標の次元>)
f = self.gaussian(represent, relation, U)
完全接続関数と活性化関数を使用してグラフの埋め込みを処理します。
f2 = F.relu(self.sg_conv_1(f))
h = F.relu(self.sg_conv_2(f2))
完全に接続された 2 つの sg_conv は次のとおりです。
self.sg_conv_1 = nn.Linear(self._fc7_channels, 512)
self.sg_conv_2 = nn.Linear(512, 256)
4. 候補領域との融合グラフ
グラフから得られた埋め込みベクトルと候補領域を融合した後、新しい予測フレームと分類データが取得されます
new_f = torch.cat([fc7, h], dim=1)
new_cls_prob, new_bbox_pred = self._new_region_classification(new_f)
for k in self._predictions.keys():
self._score_summaries[k] = self._predictions[k]
return rois, new_cls_prob, new_bbox_pred
上記で使用した _new_region_classification() の実装は次のとおりです。
def _new_region_classification(self, f):
cls_score = self.new_cls_score_net(f)
cls_pred = torch.max(cls_score, 1)[1]
cls_prob = F.softmax(cls_score, dim=1)
bbox_pred = self.new_bbox_pred_net(f)
self._predictions["cls_score"] = cls_score
self._predictions["cls_pred"] = cls_pred
self._predictions["cls_prob"] = cls_prob
self._predictions["bbox_pred"] = bbox_pred
return cls_prob, bbox_pred