{"id":18186,"date":"2020-02-28T12:05:59","date_gmt":"2020-02-28T11:05:59","guid":{"rendered":"https:\/\/www.inovex.de\/blog\/?p=18186"},"modified":"2026-03-17T08:57:27","modified_gmt":"2026-03-17T07:57:27","slug":"3d-deep-learning-tensorflow-2","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/","title":{"rendered":"3D Deep Learning with TensorFlow 2"},"content":{"rendered":"<p>The world that we interact with each and every day is three-dimensional,\u00a0but the majority of deep learning models process visual data as 2D images. However, there are some neural network architectures that are capable of processing 3D structures directly. An early approach was presented at the Conference on Computer Vision and Pattern Recognition (CVPR) in 2017 and is called PointNet.<\/p>\n<p>This blog post touches on two topics: First, we will have a look at 3D deep learning with PointNet. The creators of PointNet have also released some code on GitHub, providing a TensorFlow 1.x implementation of PointNet. Since TensorFlow 2.0 was released on September\u00a030, 2019, we will transform this original PointNet implementation into an idiomatic TensorFlow 2 implementation in the second part of the post. You can find all code examples discussed in this blog post on <a href=\"https:\/\/github.com\/RobinBaumann\/pointnet\/\">GitHub<\/a>.<!--more--><\/p>\n<p>So let\u2019s get started\u00a0 with an overview of the PointNet architecture. If you are not yet familiar with the fundamentals of deep learning systems, check out our blog posts about <a href=\"https:\/\/www.inovex.de\/blog\/deep-learning-fundamentals\/\">Deep Learning Fundamentals<\/a> and the <a href=\"https:\/\/www.inovex.de\/blog\/artificial-neural-networks-concepts-methods\/\">concepts and methods of artificial neural networks<\/a>.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\"><\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#PointNet-Deep-Learning-on-Point-Sets-for-3D-Classification-and-Segmentation\" >PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#TensorFlow-2x\" >TensorFlow 2.x<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#Automatically-upgrade-using-the-conversion-script\" >Automatically upgrade using the conversion script<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#Make-the-code-TensorFlow-20-native\" >Make the code TensorFlow 2.0-native<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#Conclusion\" >Conclusion<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#Join-us\" >Join us!<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"PointNet-Deep-Learning-on-Point-Sets-for-3D-Classification-and-Segmentation\"><\/span>PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Point clouds are an important data structure in 3D computer vision; some of the most important use cases are in autonomous driving and robotics, where 3D information is needed to navigate in and interaction with the real world.\u00a0A point cloud is an unordered set of spatial coordinates, X, Y and Z, without any explicit geometric relationship between points. However, current machine learning models assume a well defined, structured input, which makes it impossible to feed point clouds directly into them. This led to some form of resampling in terms of 3D data modality, like <a href=\"https:\/\/www.researchgate.net\/publication\/243639276_Occupancy_Grids_A_Probabilistic_Framework_for_Robot_Perception_and_Navigation\">3D occupancy<\/a> or <a href=\"https:\/\/ieeexplore.ieee.org\/document\/1087212\">voxel grid sampling<\/a>, leading to information loss due to the quantization of fine granular point cloud data. Besides, these techniques are computationally expensive and may lead to a much larger memory footprint than raw point cloud data.<\/p>\n<p>The PointNet neural network architecture contains special building blocks that remedy the need for a resampled input format. We will discuss them in the following sections. But first, have a look at the following graphic, which displays the whole PointNet architecture. All the <code>mlp(p,q,...)<\/code> layers prior to the max pooling are actually \\(1 \u00d7 1 \\) convolutional layers: they are like fully connected \u201cvanilla\u201c MLP layers, where each operates on a single point in the point cloud. PointNet computes a global feature vector using a max pooling layer. This makes the model invariant to the number of input points, since the classification is only based on one 1024-dimensional vector. One really nice thing about PointNet is that you can use the same base network for classification and segmentation tasks. The latter can be achieved by concatenating the global feature with each point feature and subsequently computing new point features based on this combined feature. This results in per-point features that contain both, local geometric information and global semantics. With small modifications to the architecture, the model is also capable of\u00a0 computing object part segmentations. Given a 3D scan, the task of object part segmentation is to assign part category labels (e.g. chair leg, cup handle) to each point.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18187\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-1024x374.jpg\" alt=\"\" width=\"950\" height=\"347\" srcset=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-1024x374.jpg 1024w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-300x110.jpg 300w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-768x281.jpg 768w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-1536x561.jpg 1536w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-2048x749.jpg 2048w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-1920x702.jpg 1920w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-400x146.jpg 400w, https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/pointnet-360x132.jpg 360w\" sizes=\"auto, (max-width: 950px) 100vw, 950px\" \/><\/p>\n<p>Now that you know the basic architecture of PointNet, we can have a look at what makes this architecture so special. The invariance against input size and order is accomplished by the application of \\(1 \u00d7 1 \\) convolutions and a symmetrical function, which maps an \\(N \u00d7 D \\)-dimensional input tensor to one global M-dimensional feature vector. \\(1 \u00d7 1 \\) convolutions (that technically aren\u2019t convolutions at all), are especially useful to alter the amount of channels of your input. For example, consider an input image of shape \\(n_h \u00d7 n_w \u00d7 n_c \\). After you have applied a convolutional layer with <i>p<\/i> \\(1 \u00d7 1 \\) filters, your output will be of shape \\(n_h \u00d7 n_w \u00d7 p \\). Another view of a \\(1 \u00d7 1 \\) convolution is that it takes an \\(n_c \\)-dimensional vector as input and transform it into a p-dimensional output vector, much like a dense neural network does. For point clouds, imagine convolving over the per-point features while increasing or decreasing the amount of features which are independent of the overall number of points in the point cloud. Clearly, this leads to a fully convolutional architecture that can be used to compute exactly the same features, regardless of the input dimensionality.<\/p>\n<p>All the point features are reduced to one global feature vector. To achieve this, global max pooling is used as symmetrical function in PointNet, which extracts only the largest component of each of the D-dimensional vectors, resulting in one D-dimensional vector containing a signature of the input space. Have a look at the graphic below for a visualization of this approach.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-18189 size-full\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/maxpool-1.png\" alt=\"\" width=\"581\" height=\"288\" \/><\/p>\n<p>The third and maybe most confusing element of the PointNet architecture may be the little guy in the image&#8217;s lower left, called T-Net. The appendix of the PointNet paper describes it as <em>mini-PointNet<\/em>. It actually consists of the same building blocks as its big brother, but what does it do? Well, the process of semantic segmentation needs to be invariant to geometric transformations applied to the input. T-Net is used to achieve exactly this. It estimates an orthogonal transformation matrix, which is subsequently applied to the input coordinates. The result is a well aligned representation of the input points. The same transformation is applied to the 64-dimensional feature space in the second T-Net. However, since the dimensionality of the feature space is much higher than that of the input space, the authors included a regularization term to stabilize the optimization process and ensure that the estimated matrix becomes close to an orthogonal rotation matrix. This is desired, as orthogonal transformations will not lose information in the input.<\/p>\n<p>Before we move on and make the original implementation TensorFlow 2.x compatible, let us have a quick look at the original implementation of PointNet. The structure of the official <a href=\"https:\/\/github.com\/charlesq34\/pointnet\">GitHub-Repository<\/a> divides the code into four different modules. The core model definitions reside in the <code>model<\/code> module, which provides each definition in a single python file. Training and evaluation of the classification can be done using the <code>train.py<\/code> and <code>evaluate.py<\/code> files respectively, which reside in the top level directory. The semantic segmentation, part segmentation together with their training, data preparation and evaluation scripts all reside in the <code>sem_seg<\/code> and <code>part_seg<\/code> modules. The <code>utils<\/code> module contains some utility functions used in the model definitions or data preparation.\u00a0We will alter this structure a little bit later on in order to reduce the amount of code duplication. But first, let\u2019s have a look at the changes applied to the TensorFlow API.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"TensorFlow-2x\"><\/span>TensorFlow 2.x<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I\u2019ve read many blog posts about the changes applied to TensorFlow with the official release of version 2.0 on September 30, 2019. By the time of this writing, the latest version of TensorFlow is 2.1, the last one to officially support Python 2.7.\u00a0 All the blog posts I have read did a really nice job at pointing out all the major changes in the TensorFlow API. I will link some of the posts that I enjoyed the most. However, I still want to recapture the most important changes for you:<\/p>\n<ul>\n<li>(tf.)keras is the preferred way to define models. Although the Keras API was integrated into TensorFlow since release 1.10, TF 2.0 marks the end of the Keras standalone era. The APIs are now fully in sync and the Keras repository will not receive any further updates. So make sure to change your import statements!<\/li>\n<li>No more globals! Finally, all the mess with implicit global namespaces in TensorFlow 1.x is gone! However, this means that you are now responsible to track your variables. If you somehow lose them, they are gone! Luckily, you can bypass this extra effort of variable tracking by simply using Keras objects as your main building blocks.<\/li>\n<li>Bye bye, Session API! My personal favorite comes with the introduction of Eager Execution to TensorFlow as default computation mode. It felt always way too cumbersome to define the whole model ahead of time and debug it with print statements. TensorFlow 2.x introduces the <code>tf.function()<\/code> decorator to mark a function for JIT compilation, so that TensorFlow runs it as a single graph. The model definition process feels now much more pythonic!<\/li>\n<\/ul>\n<p>So let\u2019s convert PointNet to TensorFlow 2.x code. I will further refer to TensorFlow 2.x as TF2.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Automatically-upgrade-using-the-conversion-script\"><\/span>Automatically upgrade using the conversion script<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>TensorFlow 2 ships with a conversion script to automatically make your existing TensorFlow 1.x projects compatible with the API changes of TF2. I forked the original PointNet repository and moved it into the original folder of the attached GitHub repository. I am now able to upgrade the project with the following command:<\/p>\n<pre>tf_upgrade_v2 --intree original --outtree tf2 --reportfile tf2_upgrade_report.txt<\/pre>\n<p>The script traverses through all python files in the specified intree directory and string-replaces all old TensorFlow 1.x API calls with its TF2 equivalents. Whenever it encounters problems during the conversion, it logs a hint to the problematic piece of code in the <code>tf2_upgrade_report.txt<\/code> file. So we will first start investigating that file.<\/p>\n<p>Most of the lines in the log file contain information about renamed API calls or changes of the parameter list. For instance, the following output arises while parsing the file containing the semantic segmentation model:<\/p>\n<pre class=\"\">--------------------------------------------------------------------------------\r\n\r\nProcessing file 'original\/sem_seg\/model.py'\r\n\r\noutputting to 'tf2\/sem_seg\/model.py'\r\n\r\n--------------------------------------------------------------------------------\r\n\r\n13:21: INFO: Renamed 'tf.placeholder' to 'tf.compat.v1.placeholder'\r\n\r\n15:16: INFO: Renamed 'tf.placeholder' to 'tf.compat.v1.placeholder'\r\n\r\n64:11: INFO: Added keywords to args of function 'tf.reduce_mean'\r\n\r\n68:12: INFO: Renamed 'tf.placeholder' to 'tf.compat.v1.placeholder'\r\n\r\n70:13: INFO: Renamed 'tf.Session' to 'tf.compat.v1.Session'\r\n\r\n71:19: INFO: Renamed 'tf.global_variables_initializer' to 'tf.compat.v1.global_variables_initializer'\r\n\r\n--------------------------------------------------------------------------------\r\n\r\n<\/pre>\n<p>Some other changes need manual checks, for example all <code>*.save()<\/code>\u00a0calls. However, this applies only if we try to save Keras models, because in TF2, Keras\u2019 <code>model.save()<\/code>\u00a0writes the model definition in the format of a TensorFlow SavedModel instead of a HDF5. PointNet only uses model checkpointing, so we can skip this for now and move on to the next warning, which requires us to check all <code>tf.get_variable()<\/code>\u00a0calls.<\/p>\n<p>In TF2, <code>tf.get_variable()<\/code>\u00a0returns a <code>tf.ResourceVariable<\/code> instead of a <code>tf.Variable<\/code>, which has well-defined semantics and is stricter about shapes. This is implemented using a <code>read_value<\/code>\u00a0operation that is added to the graph. The tensors returned by this operation are guaranteed to see all modifications applied to the variable from operations on which the <code>read_value<\/code> depends. Additionally, the Tensors are guaranteed not to adapt any modification applied to the variable from operations that depend on the read_value. In other words, operations behave as if they were executed in some total order consistent with the partial ordering constraints enforced by the graph. The whole semantics are described in the <a href=\"https:\/\/github.com\/tensorflow\/community\/blob\/master\/rfcs\/20190610-resource-variable-semantics.md\">Concurrency Semantics For TensorFlow Resource Variables<\/a>.\u00a0 The example below shows the different behavior of TensorFlow Variables in Version 1.x and 2.x.<\/p>\n<div style=\"width: 60%;padding: 0 0 0 0;float: left\">\n<pre class=\"right-set:true lang:python decode:true\">import tensorflow as tf\r\n\r\nwith tf.compat.v1.Session() as sess:\r\n\r\n    v1 = tf.Variable(0)\r\n\r\n    v2 = tf.Variable(0)\r\n\r\n    upd1 = tf.compat.v1.assign(v1, v2 + 1)\r\n\r\n    upd2 = tf.compat.v1.assign(v2, v1 + 1)\r\n\r\n    init = tf.compat.v1.global_variables_initializer()\r\n\r\n    for i in range(10):\r\n\r\n        sess.run(init)\r\n\r\n        sess.run([upd1, upd2])\r\n\r\n        print(*sess.run([v1, v2]))<\/pre>\n<\/div>\n<div style=\"width: 40%;padding: 0 0 60px 0;float: right\">\n<pre class=\"height-mode:1 height:750 left-set:true lang:python decode:true\">import tensorflow as tf\r\n\r\nv1 = tf.Variable(0)\r\n\r\nv2 = tf.Variable(0)\r\n\r\nupd1 = tf.compat.v1.assign(v1, v2 + 1)\r\n\r\nupd2 = tf.compat.v1.assign(v2, v1 + 1)\r\n\r\nfor i in range(10):\r\n\r\n    print(v1.numpy(), v2.numpy())\r\n\r\n<\/pre>\n<\/div>\n<p>On the left side you can find TensorFlow 1.x code and on the right side equivalent TF2 code. However, they behave differently. The result of the left side could be either 1, 1 for v1 and v2 respectively, or it could result in 1, 2 or even 2, 1. The right side in contrast always prints the result 1, 2.<\/p>\n<p>In the PointNet implementation,\u00a0<code>tf.get_variable()<\/code> calls are used to initialize the model parameters of the T-Net. We will refactor this code to Keras-layers, so we don\u2019t have to track all variables by ourselves. Moreover, we do not have to care about those warnings in the report file at all, because we won\u2019t do anything special with the variables, like control scopes, etc.<\/p>\n<p>Believe it or not, these are the only things that we really have to check for our PointNet implementation and the code is already running in a TF2 environment. However, there are still some things that require a manual check, like all <code>tf.summary<\/code> calls that are not automatically convertible by the script. We will deal with them later, when we refactor our training script.<\/p>\n<p>Our current TF2 PointNet implementation is basically the TF2 implementation with changed API calls. It doesn\u2019t take advantage of any of the new features that make TF2 so advantageous. In the following sections, I describe how we can achieve a \u201cTF2-native\u201c implementation. Consequently, we will get concise model definitions and training\/evaluation loops.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Make-the-code-TensorFlow-20-native\"><\/span>Make the code TensorFlow 2.0-native<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let\u2019s start the conversion with the utils module. It contains several implementations of convolutional layers, especially different kind of convolutions with batch normalization, dropout, max- and average pooling and fully connected layers. Since we will use <code>tf.keras<\/code> to build our TF2 PointNet, we will remove most of the utility functions in this module and replace them with their Keras equivalent. We will only keep the definitions for convolutional and fully connected layers with batch normalization. Have a look at the resulting code for the convolutional layer, which looks like the following:<\/p>\n<pre class=\"lang:python decode:true\">def conv1d_bn(x, num_filters, kernel_size, padding='same', strides=1, use_bias=False, scope=None, activation='relu'):\r\n\r\n\u00a0\u00a0\u00a0 \"\"\"\r\n\r\n\u00a0\u00a0\u00a0 Utility function to apply Convolution + Batch Normalization.\r\n\r\n\u00a0\u00a0\u00a0 \"\"\"\r\n\r\n    with K.name_scope(scope):\r\n\r\n        input_shape = x.get_shape().as_list()[-2:]\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 x = Conv1D(num_filters, kernel_size, strides=strides, padding=padding,\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 use_bias=use_bias, input_shape=input_shape)(x)\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 x = BatchNormalization(momentum=0.9)(x)\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 x = Activation(activation)(x)\r\n\r\n\u00a0\u00a0\u00a0 return x<\/pre>\n<p>The code for the dense layers is updated analogously. The original implementation uses 2D convolutions instead of 1D convolutions, which would be a better fit to the point cloud data structure in my opinion. Therefore, we will use 1D convolutions in our implementation. Interestingly, a short look in the TensorFlow <a href=\"https:\/\/www.tensorflow.org\/api_docs\/python\/tf\/nn\/conv1d?version=stable\">documentation<\/a> reveals that even though we call 1D convolutions, the data is reshaped and a 2D convolution is computed by TensorFlow. So our code will still be equivalent to the original implementation, but more expressive.<\/p>\n<p>We will not alter the other files in this module, since they do not contain any TensorFlow code. So let\u2019s move on and refactor the network architectures (models module) to Keras models.<\/p>\n<p>First, we will refactor the T-Net implementations. They only differ in the size of the transformation matrix and the additional regularization term in the feature transform. Therefore, we will wrap the T-net in a function and make the size of the matrix and the regularization configurable.\u00a0 For the latter, we will define a Keras Regularizer subclass in the utility package.<\/p>\n<p>Subsequently, we will separate out the base model up to the global feature vector as it is used in both the segmentation and classification task. We therefore define it as a function with configurable input dimension. It returns the output of the global max pooling operation and the output of the applied feature transform.<\/p>\n<p>Second, we refactor the task specific networks. We start with the classification network. Therefore, we call the base model that we have just extracted and add the fully connected classification part on top. We will do the same for the semantic segmentation network. The part segmentation network is a little bit more complicated, because it has multiple inputs. Alongside the points, we also feed a one-hot encoded vector into the network, which indicates the object part category. According to the PointNet creators, this is due to the lack of training data in the ShapeNet part data set. As a result, the task reduces to predicting object parts for a known object category instead of predicting object parts across all possible categories.<\/p>\n<p>We use the Keras functional API to define our models. This enables us to define small building blocks of PointNet as functions that we can reuse in different parts of the models. Another possible way to define the PointNet Architecture would be to subclass <code>tf.keras.Model<\/code>, as this provides a higher level of architectural flexibility. However, the model subclassing approach is considered harder to write and debug. The code is also more verbose, because you have to define the model architecture and the forward pass separately. Therefore, we use the functional API, as it provides enough flexibility to implement the PointNet architecture. The following snippet shows the implementation of the base model, which is shared across all task specific network architectures.<\/p>\n<pre class=\"lang:python decode:true\">def get_model(inputs):\r\n\r\n\u00a0\u00a0\u00a0 \"\"\"\r\n\r\n\u00a0\u00a0\u00a0 Convolutional portion of model, common across different tasks (classification, segmentation, etc)\r\n\r\n\u00a0\u00a0\u00a0 :param inputs: Input tensor with the point cloud shape (BxNxK)\r\n\r\n\u00a0\u00a0\u00a0 :return: tensor layer for CONV5 activations, tensor layer with local features\r\n\r\n\u00a0\u00a0\u00a0 \"\"\"\r\n\r\n\u00a0\u00a0\u00a0 # Obtain spatial point transform from inputs and convert inputs\r\n\r\n\u00a0\u00a0\u00a0 ptransform = transform_net(inputs, scope='transform_net1', regularize=False)\r\n\r\n\u00a0\u00a0\u00a0 point_cloud_transformed = Dot(axes=(2, 1))([inputs, ptransform])\r\n\r\n\u00a0\u00a0\u00a0 # First block of convolutions\r\n\r\n\u00a0\u00a0\u00a0 net = conv1d_bn(point_cloud_transformed, num_filters=64, kernel_size=1, padding='valid',\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 use_bias=True, scope='conv1')\r\n\r\n\u00a0\u00a0\u00a0 net = conv1d_bn(net, num_filters=64, kernel_size=1, padding='valid',\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 use_bias=True, scope='conv2')\r\n\r\n\u00a0\u00a0\u00a0 # Obtain feature transform and apply it to the network\r\n\r\n\u00a0\u00a0\u00a0 ftransform = transform_net(net, scope='transform_net2', regularize=True)\r\n\r\n\u00a0\u00a0\u00a0 net_transformed = Dot(axes=(2, 1))([net, ftransform])\r\n\r\n\u00a0\u00a0\u00a0 # Second block of convolutions\r\n\r\n\u00a0\u00a0\u00a0 net = conv1d_bn(net_transformed, num_filters=64, kernel_size=1, padding='valid', use_bias=True, scope='conv3')\r\n\r\n\u00a0\u00a0\u00a0 net = conv1d_bn(net, num_filters=128, kernel_size=1, padding='valid', use_bias=True, scope='conv4')\r\n\r\n\u00a0\u00a0\u00a0 hx = conv1d_bn(net, num_filters=1024, kernel_size=1, padding='valid', use_bias=True, scope='hx')\r\n\r\n\u00a0\u00a0\u00a0 # add Maxpooling here, because it is needed in both nets.\r\n\r\n\u00a0\u00a0\u00a0 net = GlobalMaxPooling1D(data_format='channels_last', name='maxpool')(hx)\r\n\r\n    return net, net_transformed\r\n\r\n<\/pre>\n<p>Last but not least, we have to refactor the training scripts. Finally, we can get rid of the sessions. We will train and evaluate the models using Keras. Remember that we still need to check the <code>tf.summary<\/code> calls? We bypass this by using the TensorBoard callback to let Keras do the logging for us. Thus, we don\u2019t need any <code>tf.summary<\/code> call at all. We replace the training and evaluation functions of the original implementation with Keras model.fit_generator calls. Therefore, we need to write a data provider to feed training and evaluation samples to our model. Due to the large size of ModelNet-40, we create a HDF5 Virtual Dataset, so that we do not need to load all point clouds into memory. Our data generator is a subclass of Keras Sequence. It contains all the augmentation and sampling logic. If you aren\u2019t familiar with Keras data Providers consider reading <a href=\"https:\/\/stanford.edu\/~shervine\/blog\/keras-how-to-generate-data-on-the-fly\">this introduction<\/a>. The following snippets contains the full training pipeline<\/p>\n<pre class=\"lang:python decode:true\">def train():\r\n\r\n\u00a0\u00a0\u00a0 model = MODEL.get_model((None, 3), NUM_CLASSES)\r\n\r\n\u00a0\u00a0\u00a0 learning_rate = get_learning_rate_schedule()\r\n\r\n\u00a0\u00a0\u00a0 optimizer = tf.keras.optimizers.Adam(learning_rate)\r\n\r\n\u00a0\u00a0\u00a0 PointCloudProvider.initialize_dataset()\r\n\r\n\u00a0\u00a0\u00a0 generator_training = PointCloudProvider('train', BATCH_SIZE, n_classes=NUM_CLASSES, sample_size=MAX_NUM_POINT)\r\n\r\n\u00a0\u00a0\u00a0 generator_validation = PointCloudProvider('test', BATCH_SIZE, n_classes=NUM_CLASSES, sample_size=MAX_NUM_POINT)\r\n\r\n\u00a0\u00a0\u00a0 callbacks = [\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 tf.keras.callbacks.ModelCheckpoint(CKPT_DIR, save_weights_only=False, save_best_only=True),\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 tf.keras.callbacks.TensorBoard(LOG_DIR)\r\n\r\n\u00a0\u00a0\u00a0 ]\r\n\r\n\u00a0\u00a0\u00a0 model.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['categorical_accuracy'])\r\n\r\n\u00a0\u00a0\u00a0 model.fit_generator(generator=generator_training, validation_data=generator_validation,\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 steps_per_epoch=len(generator_training),\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0   validation_steps=len(generator_validation),\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 epochs=MAX_EPOCH, callbacks=callbacks, use_multiprocessing=False)\r\n\r\n    model.save(\"trained_model.pb\")<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We have successfully converted the original PointNet implementation to TensorFlow 2.0. Well, because of the switch from a native TensorFlow 1.x implementation to the use of tf.keras it felt more like a complete reimplementation, but\u00a0 I learned a lot about the internals of TensorFlow 2 during the conversion process. The final result can be found on <a href=\"https:\/\/github.com\/RobinBaumann\/pointnet\/\">GitHub<\/a>. You can find both, the original and the TensorFLow 2 implementation in the repository. I also included the full output of the conversion script. Feel free to dig through the code and use the implementation for your own 3D Deep Learning, TensorFlow 2 projects.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Join-us\"><\/span>Join us!<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<ul>\n<li>Senior Data Scientist<\/li>\n<li>Senior Machine Learning Engineer<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The world that we interact with each and every day is three-dimensional,\u00a0but the majority of deep learning models process visual data as 2D images. However, there are some neural network architectures that are capable of processing 3D structures directly. An early approach was presented at the Conference on Computer Vision and Pattern Recognition (CVPR) in [&hellip;]<\/p>\n","protected":false},"author":137,"featured_media":18264,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[509,206,151],"service":[76,431],"coauthors":[{"id":137,"display_name":"Robin Baumann","user_nicename":"rbaumann"}],"class_list":["post-18186","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-ai-2","tag-data-science","tag-deep-learning","service-artificial-intelligence","service-data-science"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>3D Deep Learning with TensorFlow 2 - inovex GmbH<\/title>\n<meta name=\"description\" content=\"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"3D Deep Learning with TensorFlow 2 - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2020-02-28T11:05:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-17T07:57:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Robin Baumann\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet-1024x576.png\" \/>\n<meta name=\"twitter:creator\" content=\"@_RobinBaumann\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Robin Baumann\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"17\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Robin Baumann\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/\"},\"author\":{\"name\":\"Robin Baumann\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/bf2965260253341edd321d75f38dca81\"},\"headline\":\"3D Deep Learning with TensorFlow 2\",\"datePublished\":\"2020-02-28T11:05:59+00:00\",\"dateModified\":\"2026-03-17T07:57:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/\"},\"wordCount\":2794,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/3d-deep-learning-pointnet.png\",\"keywords\":[\"Ai\",\"Data Science\",\"Deep Learning\"],\"articleSection\":[\"Analytics\",\"English Content\",\"General\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/\",\"name\":\"3D Deep Learning with TensorFlow 2 - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/3d-deep-learning-pointnet.png\",\"datePublished\":\"2020-02-28T11:05:59+00:00\",\"dateModified\":\"2026-03-17T07:57:27+00:00\",\"description\":\"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/3d-deep-learning-pointnet.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2020\\\/02\\\/3d-deep-learning-pointnet.png\",\"width\":1920,\"height\":1080,\"caption\":\"3D Deep. Learning set in a 3D font\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/3d-deep-learning-tensorflow-2\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"3D Deep Learning with TensorFlow 2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/inovexde\",\"https:\\\/\\\/x.com\\\/inovexgmbh\",\"https:\\\/\\\/www.instagram.com\\\/inovexlife\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/inovex\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/bf2965260253341edd321d75f38dca81\",\"name\":\"Robin Baumann\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/BWsmall-96x96.jpg6394835c952c64696603cb897fef6e76\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/BWsmall-96x96.jpg\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/BWsmall-96x96.jpg\",\"caption\":\"Robin Baumann\"},\"description\":\"Hi there! My name is Robin and I work as a Data Scientist for inovex. I am particularly interested in applications of Deep Learning in the area of visual computing. Therefore, most of my blog articles deal with the things that happen when you call model.fit() on image or 3D data.\",\"sameAs\":[\"https:\\\/\\\/www.linkedin.com\\\/in\\\/robin-baumann-272959159\\\/\",\"https:\\\/\\\/x.com\\\/_RobinBaumann\"],\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/author\\\/rbaumann\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"3D Deep Learning with TensorFlow 2 - inovex GmbH","description":"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/","og_locale":"de_DE","og_type":"article","og_title":"3D Deep Learning with TensorFlow 2 - inovex GmbH","og_description":"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.","og_url":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2020-02-28T11:05:59+00:00","article_modified_time":"2026-03-17T07:57:27+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png","type":"image\/png"}],"author":"Robin Baumann","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet-1024x576.png","twitter_creator":"@_RobinBaumann","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Robin Baumann","Gesch\u00e4tzte Lesezeit":"17\u00a0Minuten","Written by":"Robin Baumann"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/"},"author":{"name":"Robin Baumann","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/bf2965260253341edd321d75f38dca81"},"headline":"3D Deep Learning with TensorFlow 2","datePublished":"2020-02-28T11:05:59+00:00","dateModified":"2026-03-17T07:57:27+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/"},"wordCount":2794,"commentCount":0,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png","keywords":["Ai","Data Science","Deep Learning"],"articleSection":["Analytics","English Content","General"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/","url":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/","name":"3D Deep Learning with TensorFlow 2 - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png","datePublished":"2020-02-28T11:05:59+00:00","dateModified":"2026-03-17T07:57:27+00:00","description":"In this blog post, we will first have a look at 3D deep learning with PointNet. Its creators provide a TensorFlow 1.x implementation of PointNet on Github, but since TensorFlow 2.0 was released in the meantime, we will transform it into an idiomatic TensorFlow 2 implementation in the second part of this post.","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2020\/02\/3d-deep-learning-pointnet.png","width":1920,"height":1080,"caption":"3D Deep. Learning set in a 3D font"},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/3d-deep-learning-tensorflow-2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"3D Deep Learning with TensorFlow 2"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/bf2965260253341edd321d75f38dca81","name":"Robin Baumann","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/wp-content\/uploads\/BWsmall-96x96.jpg6394835c952c64696603cb897fef6e76","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/BWsmall-96x96.jpg","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/BWsmall-96x96.jpg","caption":"Robin Baumann"},"description":"Hi there! My name is Robin and I work as a Data Scientist for inovex. I am particularly interested in applications of Deep Learning in the area of visual computing. Therefore, most of my blog articles deal with the things that happen when you call model.fit() on image or 3D data.","sameAs":["https:\/\/www.linkedin.com\/in\/robin-baumann-272959159\/","https:\/\/x.com\/_RobinBaumann"],"url":"https:\/\/www.inovex.de\/de\/blog\/author\/rbaumann\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/18186","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/137"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=18186"}],"version-history":[{"count":5,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/18186\/revisions"}],"predecessor-version":[{"id":66581,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/18186\/revisions\/66581"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/18264"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=18186"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=18186"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=18186"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=18186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}