Geo_CNN代码解释与说明
1.tf_geoconv.py注释
'''
tf_geoconv.py注释
'''
from __future__ import print_function
import tensorflow as tf
from tensorflow.python.framework import ops
import sys
import os
import numpy as np
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'))
# so文件就是常说的动态链接库(共享库), 都是C或C++编译出来的。
# 该aggregate函数是采用tf的c++端定义的aggregate
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 float32
# 从.cpp文件可以看出output feature中的num_channels=输入的num_channels/6
# 6为论文中的6个基向量
norm feature: batch_size * num_points
'''
return geoconv_module.aggregate(feat, xyz, radius, decay_radius, delta)
# 使用修饰器, @ops.RegisterGradient("OpName"),建立梯度反向传播函数。
# 这里涉及到使用重写梯度的方法tf.RegisterGradient()
@tf.RegisterGradient('Aggregate')
def _aggregate_grad(op, *out_g): # 梯度函数的签名是 def _computer_gradient(op, grad)
# 其中第一个op用来接收当前操作的输入值/张量等,op.input包含输入值、输出值
# 第二个gard用来接收从反向而言的上一层的梯度。
# grad,这里为*out_g包含上层传来的梯度
feat = op.inputs[0] # 取出feature=batch_size*num_points*num_channels
xyz = op.inputs[1] # 取出xyz=batch_size * num_points * 3
top_g = out_g[0] # 取出反向而言的上一层的梯度第1个参数
norm_buffer = out_g[1] # 取出反向而言的上一层的梯度第2个参数
# 属性的信息可以通过 op.get_attr 获取.
radius = op.get_attr("radius") # 获取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]
# 验证梯度差异
# tf.test.TestCase继承了 unittest.TestCase,但添加了一些额外的方法
# 可以添加与 TensorFlow 测试相关的方法
class AggregateTest(tf.test.TestCase):
def test(self):
pass
def test_grad(self):
with tf.device('/gpu:0'):
# features : batch_size=8, num_points=128, num_channels = 192
feats = tf.constant(np.random.random((8, 128, 192)).astype('float32'))
# xyz : batch_size=8, num_points=128, num_channels = 3(xyz)
xyz = tf.constant(np.random.random((8, 128, 3)).astype('float32'))
# radius = 0.3, decay_radius = 0.6
ag, _ = aggregate(feats, xyz, 0.3, 0.6)
# ag为output feature: batch_size * num_points * num_channels
# 通过论文,我们可以知道ag=[8,128,192/6=32],因为6个通道将空间分为8个空间
print(ag)
# tf的测试格式
with self.test_session():
print("------ Going to compute gradient error")
# compute_gradient_error()数值化地计算梯度,返回与理论上的梯度的差别,我们所期望的是一个非常小的差别
# 注意这个函数计算的并不是梯度,而是输出对输入的Jacobian
# 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))
# 参考链接:https://blog.csdn.net/lenbow/article/details/52218551#reply
print(err)
# python比较断言assertLess (first, second, msg = None)
# 验证first < second,否则fail,抛出异常
# 其他断言类型:https://blog.csdn.net/weixin_34237596/article/details/93426712
self.assertLess(err, 1e-4)
if __name__ == "__main__":
tf.test.main()
2.geoconv_utils.py的geoconv注释
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) TF tensor
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
...
'''
# TensorFlow提供了variable_scope 这种独特的机制来共享变量。
with tf.variable_scope(scope) as sc:
# perceptron为感知机
self = perceptron(feat, num_outputs, scope='self', is_training=is_training, bn=False, activation_fn=None)
# mutal
mutual = perceptron(feat, bypass_num_outputs * 6, scope='mutual', is_training=is_training, bn=False, activation_fn=None)
# ag为输出的特征,其中mutual为输入特征
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]
# -1 代表的含义是不用我们自己指定这一维的大小,函数会自动计算,
# 但列表中只能存在一个-1
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=[b=batch_size, n=num_points, bc=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
3.tf_utils.py的geoconv注释
# 实现感知机
# tf的data_format有NHWC[batch, in_height, in_width, in_channels]
# 和NCHW[batch, in_channels, in_height, in_width]两种
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):
# variable_scope变量作用范围
# 通过捕获范围并设置重用来共享变量sc
with tf.variable_scope(scope) as sc:
assert(data_format=='NHWC' or data_format=='NCHW')
# 获取输入格式[B, N, C]
b, n, c = inputs.get_shape().as_list()
# 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。与矩阵各元素相乘不一样,tf.atmul为矩阵乘法
# tf.multiply()两个矩阵中对应元素各自相乘
outputs = tf.matmul(inputs, weights) # outputs=[B*N,num_output_channels]
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: # 如果bn归一化
# batch_norm_for_fc(inputs, is_training, bn_decay, scope)
# 返回值为batch_norm_template(inputs, is_training, scope, [0,], bn_decay, data_format='NHWC')
# outputs = [inputs, is_training, scope, [0,], bn_decay, data_format='NHWC']
outputs = batch_norm_for_fc(outputs, is_training, bn_decay, 'bn')
# 若有激活函数,则添加相应的激活函数层,这里默认为Relu
if activation_fn is not None:
outputs = activation_fn(outputs)
return outputs
其他待更