该Geo_CNN源码来源于https://github.com/voidrank/Geo-CNN
1、perceptron函数:感知机
def perceptron(inputs,
num_output_channels,
scope,
is_training=None,
data_format='NHWC',
user_xavier=True,
stddev=1e-3,
weight_decay=None,
activation_fn=tf.nn.relu,
use_xavier=True,
bn=False,
bn_decay=None):
with tf.variable_scope(scope) as sc:
assert(data_format=='NHWC' or data_format=='NCHW')
#obtain [B,N,C]
b, n, c = inputs.get_shape().as_list()
#tf.reshape(tensor, shape, name=None)函数的作用是将tensor变换为参数shape的形式
#inputs:[B*N,C]
inputs = tf.reshape(inputs, (-1, c))
#_variable_with_weight_decay 为变量添加weight decay
#weights=[C,num_output_channels]
weights = _variable_with_weight_decay('weights',
shape=[c, num_output_channels],
use_xavier=use_xavier,
stddev=stddev,
wd=weight_decay)
#tf.matmul()将矩阵a乘以矩阵b,生成a * b。
#outputs=[B*N,num_output_channels]
outputs = tf.matmul(inputs, weights)
biases = _variable_on_cpu('biases', [num_output_channels],
tf.constant_initializer(0.0))
#添加偏置项
outputs = tf.nn.bias_add(outputs, biases)
#outputs=[B,N,num_output_channels]
outputs = tf.reshape(outputs, (b, n, -1))
#bn为True时,添加相应的bn层归一化
if bn:
""" Batch normalization on FC data.
Args:
inputs: Tensor, 2D BxC input
is_training: boolean tf.Varialbe, true indicates training phase
bn_decay: float or float tensor variable, controling moving average weight
scope: string, variable scope变量作用域
Return:
normed: batch-normalized maps
"""
outputs = batch_norm_for_fc(outputs, is_training, bn_decay, 'bn')
#若有激活函数,则添加相应的激活函数层,这里默认为Relu
if activation_fn is not None:
outputs = activation_fn(outputs)
return outputs
二、aggregate
from __future__ import print_function
import tensorflow as tf
from tensorflow.python.framework import ops
import sys
import os
import numpy as np
##os.path.dirname(__file__)返回的是.py文件的目录:C:\Users\owolf\Desktop
#os.path.abspath(__file__)返回的是.py文件的绝对路径(完整路径):C:\Users\owolf\Desktop\1.py
#组合使用os.path.dirname(os.path.abspath(__file__)):C:\Users\owolf\Desktop
#os.path.join()拼接路径os.path.join(os.path.dirname(os.path.abspath(__file__)),'1.py'):C:\Users\owolf\Desktop\1.py
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
#表示导入对应目录下的模块
sys.path.append(BASE_DIR)
geoconv_module=tf.load_op_library(os.path.join(BASE_DIR, 'tf_geoconv_so.so'))
def aggregate(feat, xyz, radius, decay_radius, delta=0):
'''
inputs:
feature: batch_size * num_points * num_channels float32
xyz: batch_size * num_points * 3 float32
radius: float32
decay_radius: float32
delta int
returns:
output feature: batch_size * num_points * (num_channels/6) float32
#6代表的是6个基底的意思
norm feature: batch_size * num_points
'''
return geoconv_module.aggregate(feat, xyz, radius, decay_radius, delta)
#tf.RegisterGradient()注册新的梯度
#使用修饰器, @ops.RegisterGradient("OpName"),建立梯度反向传播函数。
#其中op.input包含输入值、输出值,grad包含上层传来的梯度
@tf.RegisterGradient('Aggregate')
#梯度函数的签名是 def _computer_gradient(op, grad),第一个用来接收 要计算梯度的 op,第二个用来接收 上一层传回来的梯度。
def _aggregate_grad(op, *out_g):
feat = op.inputs[0] # 取出当前的输入
xyz = op.inputs[1]
top_g = out_g[0]
norm_buffer = out_g[1]
#op.get_attr()获取 op 的属性
radius = op.get_attr("radius")
decay_radius = op.get_attr("decayradius")
delta = op.get_attr("delta")
return [geoconv_module.aggregate_grad(feat, xyz, top_g, radius, decay_radius, delta)[0], None]
该aggregate函数是采用tf的c++端定义的aggregate.下面是验证梯度差异:
class AggregateTest(tf.test.TestCase):
def test(self):
pass
def test_grad(self):
with tf.device('/gpu:0'):
feats = tf.constant(np.random.random((8, 128, 192)).astype('float32'))
xyz = tf.constant(np.random.random((8, 128, 3)).astype('float32'))
#ag=[8,128,192/6=32]
ag, _ = aggregate(feats, xyz, 0.3, 0.6)
print(ag)
with self.test_session():
print("------ Going to compute gradient error")
#compute_gradient_error()数值化地计算梯度,返回与理论上的梯度的差别,我们所期望的是一个非常小的差别
#tf.test.compute_gradient_error(x, x_shape, y, y_shape, x_init_value=None, delta=0.001, init_targets=None)
#计算梯度的error。在计算所得的与数值估计的Jacobian中 为dy/dx计算最大的error
err = tf.test.compute_gradient_error(feats, (8, 128, 192), ag, (8, 128, 32))
print(err)
self.assertLess(err, 1e-4)
if __name__ == "__main__":
tf.test.main()
三、geo_conv
def geoconv(feat, xyz, num_outputs, bypass_num_outputs,
radius, decay_radius, bn=True, is_training=False,
scope=None, bn_decay=None, activation_fn=tf.nn.relu,
delta=False):
''' GeoCNN Geo-Conv
Input:
feat: (batch_size, num_point, input_channels) 输入特征
points: (batch_size, num_point, 3) TF tensor
num_outputs: the count of output channels
bypass_num_outputs: the count of output channels of bypass
radius: the inner radius of local ball of each point.
decay radius: the outer radius of local ball of each point
...
'''
# tf.variable_scope() 的作用是为了实现变量共享
with tf.variable_scope(scope) as sc:
#感知机self=[Batchsize,Num_points,num_outputs],self代表中心点
self = perceptron(feat, num_outputs, scope='self', is_training=is_training, bn=False, activation_fn=None)
#matual=[Batchsize,Num_points, bypass_num_outputs * 6],
mutual = perceptron(feat, bypass_num_outputs * 6, scope='mutual', is_training=is_training, bn=False, activation_fn=None)
#ag:[batch_size, num_points, bypass_num_outputs]
ag, _ = aggregate(mutual, xyz, radius, decay_radius, delta)
#b=batch_size, n=num_points, bc=bypass_num_outputs
b, n, bc = ag.get_shape().as_list()
#c=num_outputs
_, _, c = self.get_shape().as_list()
#ag=[batch_size * num_points, bypass_num_outputs]
ag = tf.reshape(ag, (-1, bc))
#self=[Batchsize * Num_points, num_outputs]
self = tf.reshape(self, (-1, c))
if bn:
ag = batch_norm_for_fc(ag, is_training, bn_decay, scope='mutual_bn')
if activation_fn is not None:
ag = activation_fn(ag)
#ag=[batch_size , num_points, bypass_num_outputs]
ag = tf.reshape(ag, (b, n, bc))
#ag=[batch_size , num_points, num_outputs]
ag = perceptron(ag, num_outputs, scope='enlarge', is_training=is_training, bn=False, activation_fn=None)
#ag=[batch_size * num_points, num_outputs]
ag = tf.reshape(ag, (-1, c))
#outputs=[batch_size * num_points, num_outputs]
outputs = self + ag
if bn:
outputs = batch_norm_for_fc(outputs, is_training, bn_decay, scope='enlarge_bn')
if activation_fn is not None:
outputs = activation_fn(outputs)
#[batch_size , num_points, num_outputs]
outputs = tf.reshape(outputs, (b, n, c))
return outputs
对应论文当中的
首先:它表示的是提取中心权值矩阵Wc得到Cout
其次它表示的是提取其他邻近点的矩阵Wx,Wy,Wz得到Creduc
接着它表示的是得到Wenl矩阵得到Cout,对应论文的公式是:
输入是g(p,q),Wenl是
最后,将特征相加得到局部特征和对应公式如下: