【问题标题】:How to visualize a neural network如何可视化神经网络
【发布时间】:2015-07-05 11:06:36
【问题描述】:

我想为神经网络绘制一张动态图,以观察学习过程中权重的变化和神经元的激活。如何在 Python 中模拟该过程?

更准确地说,如果网络形状是:[1000, 300, 50], 然后我想画一个三层神经网络,分别包含 1000、300 和 50 个神经元。 此外,我希望这张图片能反映每个epoch中每一层的神经元饱和度。

我不知道该怎么做。有人能帮我解释一下吗?

【问题讨论】:

  • 它们是否需要同时全部可见(带有值)?
  • @Caramiriel 是的。是否有任何软件包支持此需求?

标签: python image neural-network


【解决方案1】:

将带有节点的网络绘制为用线连接的圆圈。线宽必须与权重成比例。即使没有线条也可以显示非常小的权重。

【讨论】:

    【解决方案2】:

    Python 库matplotlib 提供了绘制圆和线的方法。它还允许动画。

    我编写了一些示例代码来说明如何做到这一点。我的代码生成了一个简单的神经网络静态图,其中每个神经元都连接到前一层中的每个神经元。需要进一步的工作才能对其进行动画处理。

    I've also made it available in a Git repository.

    from matplotlib import pyplot
    from math import cos, sin, atan
    
    
    class Neuron():
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def draw(self):
            circle = pyplot.Circle((self.x, self.y), radius=neuron_radius, fill=False)
            pyplot.gca().add_patch(circle)
    
    
    class Layer():
        def __init__(self, network, number_of_neurons):
            self.previous_layer = self.__get_previous_layer(network)
            self.y = self.__calculate_layer_y_position()
            self.neurons = self.__intialise_neurons(number_of_neurons)
    
        def __intialise_neurons(self, number_of_neurons):
            neurons = []
            x = self.__calculate_left_margin_so_layer_is_centered(number_of_neurons)
            for iteration in xrange(number_of_neurons):
                neuron = Neuron(x, self.y)
                neurons.append(neuron)
                x += horizontal_distance_between_neurons
            return neurons
    
        def __calculate_left_margin_so_layer_is_centered(self, number_of_neurons):
            return horizontal_distance_between_neurons * (number_of_neurons_in_widest_layer - number_of_neurons) / 2
    
        def __calculate_layer_y_position(self):
            if self.previous_layer:
                return self.previous_layer.y + vertical_distance_between_layers
            else:
                return 0
    
        def __get_previous_layer(self, network):
            if len(network.layers) > 0:
                return network.layers[-1]
            else:
                return None
    
        def __line_between_two_neurons(self, neuron1, neuron2):
            angle = atan((neuron2.x - neuron1.x) / float(neuron2.y - neuron1.y))
            x_adjustment = neuron_radius * sin(angle)
            y_adjustment = neuron_radius * cos(angle)
            line = pyplot.Line2D((neuron1.x - x_adjustment, neuron2.x + x_adjustment), (neuron1.y - y_adjustment, neuron2.y + y_adjustment))
            pyplot.gca().add_line(line)
    
        def draw(self):
            for neuron in self.neurons:
                neuron.draw()
                if self.previous_layer:
                    for previous_layer_neuron in self.previous_layer.neurons:
                        self.__line_between_two_neurons(neuron, previous_layer_neuron)
    
    
    class NeuralNetwork():
        def __init__(self):
            self.layers = []
    
        def add_layer(self, number_of_neurons):
            layer = Layer(self, number_of_neurons)
            self.layers.append(layer)
    
        def draw(self):
            for layer in self.layers:
                layer.draw()
            pyplot.axis('scaled')
            pyplot.show()
    
    if __name__ == "__main__":
        vertical_distance_between_layers = 6
        horizontal_distance_between_neurons = 2
        neuron_radius = 0.5
        number_of_neurons_in_widest_layer = 4
        network = NeuralNetwork()
        network.add_layer(3)
        network.add_layer(4)
        network.add_layer(1)
        network.draw()
    

    【讨论】:

      【解决方案3】:

      为了实现Mykhaylo 的建议,我稍微修改了Milo's code 以允许提供权重作为影响每条线宽的参数。这个参数是可选的,因为没有为最后一层提供权重的意义。 这一切都是为了能够在神经网络上可视化我对this exercise 的解决方案。我给出了二进制权重(0 或 1),因此根本不会绘制权重为零的线(使图像更清晰)。

      from matplotlib import pyplot
      from math import cos, sin, atan
      import numpy as np
      
      
      class Neuron():
          def __init__(self, x, y):
              self.x = x
              self.y = y
      
          def draw(self):
              circle = pyplot.Circle((self.x, self.y), radius=neuron_radius, fill=False)
              pyplot.gca().add_patch(circle)
      
      
      class Layer():
          def __init__(self, network, number_of_neurons, weights):
              self.previous_layer = self.__get_previous_layer(network)
              self.y = self.__calculate_layer_y_position()
              self.neurons = self.__intialise_neurons(number_of_neurons)
              self.weights = weights
      
          def __intialise_neurons(self, number_of_neurons):
              neurons = []
              x = self.__calculate_left_margin_so_layer_is_centered(number_of_neurons)
              for iteration in range(number_of_neurons):
                  neuron = Neuron(x, self.y)
                  neurons.append(neuron)
                  x += horizontal_distance_between_neurons
              return neurons
      
          def __calculate_left_margin_so_layer_is_centered(self, number_of_neurons):
              return horizontal_distance_between_neurons * (number_of_neurons_in_widest_layer - number_of_neurons) / 2
      
          def __calculate_layer_y_position(self):
              if self.previous_layer:
                  return self.previous_layer.y + vertical_distance_between_layers
              else:
                  return 0
      
          def __get_previous_layer(self, network):
              if len(network.layers) > 0:
                  return network.layers[-1]
              else:
                  return None
      
          def __line_between_two_neurons(self, neuron1, neuron2, linewidth):
              angle = atan((neuron2.x - neuron1.x) / float(neuron2.y - neuron1.y))
              x_adjustment = neuron_radius * sin(angle)
              y_adjustment = neuron_radius * cos(angle)
              line_x_data = (neuron1.x - x_adjustment, neuron2.x + x_adjustment)
              line_y_data = (neuron1.y - y_adjustment, neuron2.y + y_adjustment)
              line = pyplot.Line2D(line_x_data, line_y_data, linewidth=linewidth)
              pyplot.gca().add_line(line)
      
          def draw(self):
              for this_layer_neuron_index in range(len(self.neurons)):
                  neuron = self.neurons[this_layer_neuron_index]
                  neuron.draw()
                  if self.previous_layer:
                      for previous_layer_neuron_index in range(len(self.previous_layer.neurons)):
                          previous_layer_neuron = self.previous_layer.neurons[previous_layer_neuron_index]
                          weight = self.previous_layer.weights[this_layer_neuron_index, previous_layer_neuron_index]
                          self.__line_between_two_neurons(neuron, previous_layer_neuron, weight)
      
      
      class NeuralNetwork():
          def __init__(self):
              self.layers = []
      
          def add_layer(self, number_of_neurons, weights=None):
              layer = Layer(self, number_of_neurons, weights)
              self.layers.append(layer)
      
          def draw(self):
              for layer in self.layers:
                  layer.draw()
              pyplot.axis('scaled')
              pyplot.show()
      
      
      if __name__ == "__main__":
          vertical_distance_between_layers = 6
          horizontal_distance_between_neurons = 2
          neuron_radius = 0.5
          number_of_neurons_in_widest_layer = 4
          network = NeuralNetwork()
          # weights to convert from 10 outputs to 4 (decimal digits to their binary representation)
          weights1 = np.array([\
                               [0,0,0,0,0,0,0,0,1,1],\
                               [0,0,0,0,1,1,1,1,0,0],\
                               [0,0,1,1,0,0,1,1,0,0],\
                               [0,1,0,1,0,1,0,1,0,1]])
          network.add_layer(10, weights1)
          network.add_layer(4)
          network.draw()
      

      【讨论】:

        【解决方案4】:

        我根据米洛的回答改编了一些部分

        from matplotlib import pyplot
        from math import cos, sin, atan
        
        
        class Neuron():
            def __init__(self, x, y):
                self.x = x
                self.y = y
        
            def draw(self, neuron_radius):
                circle = pyplot.Circle((self.x, self.y), radius=neuron_radius, fill=False)
                pyplot.gca().add_patch(circle)
        
        
        class Layer():
            def __init__(self, network, number_of_neurons, number_of_neurons_in_widest_layer):
                self.vertical_distance_between_layers = 6
                self.horizontal_distance_between_neurons = 2
                self.neuron_radius = 0.5
                self.number_of_neurons_in_widest_layer = number_of_neurons_in_widest_layer
                self.previous_layer = self.__get_previous_layer(network)
                self.y = self.__calculate_layer_y_position()
                self.neurons = self.__intialise_neurons(number_of_neurons)
        
            def __intialise_neurons(self, number_of_neurons):
                neurons = []
                x = self.__calculate_left_margin_so_layer_is_centered(number_of_neurons)
                for iteration in xrange(number_of_neurons):
                    neuron = Neuron(x, self.y)
                    neurons.append(neuron)
                    x += self.horizontal_distance_between_neurons
                return neurons
        
            def __calculate_left_margin_so_layer_is_centered(self, number_of_neurons):
                return self.horizontal_distance_between_neurons * (self.number_of_neurons_in_widest_layer - number_of_neurons) / 2
        
            def __calculate_layer_y_position(self):
                if self.previous_layer:
                    return self.previous_layer.y + self.vertical_distance_between_layers
                else:
                    return 0
        
            def __get_previous_layer(self, network):
                if len(network.layers) > 0:
                    return network.layers[-1]
                else:
                    return None
        
            def __line_between_two_neurons(self, neuron1, neuron2):
                angle = atan((neuron2.x - neuron1.x) / float(neuron2.y - neuron1.y))
                x_adjustment = self.neuron_radius * sin(angle)
                y_adjustment = self.neuron_radius * cos(angle)
                line = pyplot.Line2D((neuron1.x - x_adjustment, neuron2.x + x_adjustment), (neuron1.y - y_adjustment, neuron2.y + y_adjustment))
                pyplot.gca().add_line(line)
        
            def draw(self, layerType=0):
                for neuron in self.neurons:
                    neuron.draw( self.neuron_radius )
                    if self.previous_layer:
                        for previous_layer_neuron in self.previous_layer.neurons:
                            self.__line_between_two_neurons(neuron, previous_layer_neuron)
                # write Text
                x_text = self.number_of_neurons_in_widest_layer * self.horizontal_distance_between_neurons
                if layerType == 0:
                    pyplot.text(x_text, self.y, 'Input Layer', fontsize = 12)
                elif layerType == -1:
                    pyplot.text(x_text, self.y, 'Output Layer', fontsize = 12)
                else:
                    pyplot.text(x_text, self.y, 'Hidden Layer '+str(layerType), fontsize = 12)
        
        class NeuralNetwork():
            def __init__(self, number_of_neurons_in_widest_layer):
                self.number_of_neurons_in_widest_layer = number_of_neurons_in_widest_layer
                self.layers = []
                self.layertype = 0
        
            def add_layer(self, number_of_neurons ):
                layer = Layer(self, number_of_neurons, self.number_of_neurons_in_widest_layer)
                self.layers.append(layer)
        
            def draw(self):
                pyplot.figure()
                for i in range( len(self.layers) ):
                    layer = self.layers[i]
                    if i == len(self.layers)-1:
                        i = -1
                    layer.draw( i )
                pyplot.axis('scaled')
                pyplot.axis('off')
                pyplot.title( 'Neural Network architecture', fontsize=15 )
                pyplot.show()
        
        class DrawNN():
            def __init__( self, neural_network ):
                self.neural_network = neural_network
        
            def draw( self ):
                widest_layer = max( self.neural_network )
                network = NeuralNetwork( widest_layer )
                for l in self.neural_network:
                    network.add_layer(l)
                network.draw()
        

        现在图层也被标记了,轴被删除并且构建图更容易。只需通过以下方式完成:

        network = DrawNN( [2,8,8,1] )
        network.draw()
        

        这里构造了一个具有以下结构的网络:

        • 输入层中有 2 个神经元
        • 第一个隐藏层中的 8 个神经元
        • 第二个隐藏层中有 8 个神经元
        • 输出层1个神经元

        【讨论】:

        • 嗨,最近发现了你的神经网络可视化。我已经尝试过了,但它对我不起作用。更具体地说,我得到的错误是 name 'plotNN' is not defined。我检查了缩进,但我无法让它工作@OliBlum
        • DrawNN( [2,8,8,1] ).draw() 有效。我也在我的帖子中对此进行了调整
        • 感谢它的工作,您只需要更改:for iteration in xrange(number_of_neurons) 到 for iteration in range(number_of_neurons)
        • 有什么办法可以改善剧情的外观吗?我使用一个大神经网络,有 1 个输入、3 个隐藏层和 1 个输出层,神经元数量为 (36,60,50,40,30,4)
        【解决方案5】:

        这是一个基于 matplotlib 的库,名为viznet(pip install viznet)。首先,您可以阅读此notebook。这是一个例子

        Viznet 定义了一组刷机规则

        node1 >> (0, 1.2)  # put a node centered at axis (0, 1.2)
        node2 >> (2, 0)    # put a node centered at axis (2, 0)
        edge >> (node1, node2)  # connect two nodes
        

        这里,node1和node2是两个node画笔,比如 node1 = NodeBrush('nn.input', ax=d.ax, size='normal')

        第一个参数定义了节点的主题。对于一个神经网络节点(主题以'nn.'开头),其样式参考Neural Network Zoo Page

        对于edges,我们可以像edge = EdgeBrush('->', ax=d.ax, lw=2)这样定义它的画笔 第一个参数是主题,'-'代表直线,'.'虚线,'='双线,'>','' 和 '->-' 分别代表末端有箭头和中心有箭头的线。下面是几个例子

        只有节点和边是不够的,连接的规则起着根本性的作用。除了基本的连接规则,您可以在节点上创建引脚。我会在这里停下来,把它留作文件。这些灵活的特性使其能够绘制张量网络量子电路

        这个项目刚刚接受了它的 v0.1 版本,我会继续改进它。 您可以访问其 Github repo 获取最新版本,欢迎拉取请求发布问题

        【讨论】:

          【解决方案6】:

          我也遇到了同样的问题,但没有找到好的解决方案,所以我创建了一个库来做简单的绘图。下面是一个如何绘制 3 层 NN 的示例:

          from nnv import NNV
          
          layersList = [
              {"title":"input\n(relu)", "units": 3, "color": "darkBlue"},
              {"title":"hidden 1\n(relu)", "units": 3},
              {"title":"hidden 2\n(relu)", "units": 3, "edges_color":"red", "edges_width":2},
              {"title":"output\n(sigmoid)", "units": 1,"color": "darkBlue"},
          ]
          
          NNV(layersList).render(save_to_file="my_example.png")
          

          您可以通过以下方式安装该库:

          pip install nnv
          

          并在以下位置找到有关它的更多信息: https://github.com/renatosc/nnv/

          【讨论】:

          • 伟大的工作。唯一的问题是如何在圆圈后面发送线条边缘?
          • 你的意思是把圆圈放在线条的前面?
          • 是的。线条在圆圈/节点的前面,看起来不太好看。是否可以发送圆圈后面的线?
          • 另外,我想设置white color in nodes (input layer)black color in node edges/borders。但我做不到,做不到。
          • 我会在这个周末检查以添加这些调整。也可以在 github repo 上打开一个问题
          【解决方案7】:

          This 解决方案涉及 Python 和 LaTeX。对你的情况来说可能有点矫枉过正,但结果真的很美,适合更复杂的现代架构(深度学习等),所以我想这里值得一提。你首先需要在 Python 中定义你的网络,比如这个:

          import sys
          sys.path.append('../')
          from pycore.tikzeng import *
          
          # defined your arch
          arch = [
              to_head( '..' ),
              to_cor(),
              to_begin(),
              to_Conv("conv1", 512, 64, offset="(0,0,0)", to="(0,0,0)", height=64, depth=64, width=2 ),
              to_Pool("pool1", offset="(0,0,0)", to="(conv1-east)"),
              to_Conv("conv2", 128, 64, offset="(1,0,0)", to="(pool1-east)", height=32, depth=32, width=2 ),
              to_connection( "pool1", "conv2"), 
              to_Pool("pool2", offset="(0,0,0)", to="(conv2-east)", height=28, depth=28, width=1),
              to_SoftMax("soft1", 10 ,"(3,0,0)", "(pool1-east)", caption="SOFT"  ),
              to_connection("pool2", "soft1"),    
              to_end()
              ]
          
          def main():
              namefile = str(sys.argv[0]).split('.')[0]
              to_generate(arch, namefile + '.tex' )
          
          if __name__ == '__main__':
              main()
          

          之后,生成一个 TikZ 图像...

          bash ../tikzmake.sh my_arch
          

          ...这将为您生成包含网络的 PDF:

          repo 中提供了示例,位于其中一个下方。我已经在 OS X 上测试过了,应该也可以在 Linux 上运行。不知道Windows怎么样。当然,您需要安装 LaTeX 发行版。

          【讨论】:

            【解决方案8】:

            这就是我的做法:

            • 前往 Alex 的在线图表创建者:HERE
            • 画出你的
              • 使用 FCNN(全连接神经网络)的浅层网络(由简单的输入-隐藏-输出层组成)
              • 或使用 LeNet 或 AlexNet 样式的深度/卷积网络。 这就是你现在所拥有的:
            • 使用 draw.io 上的在线工具编辑 svg 文件。为此,只需将 svg 文件导入您的工作区。最终结果应如下所示:

            【讨论】:

            • 有没有办法将我自己的权重添加到这个图表中?
            【解决方案9】:

            这是一个用 Keras 构建的神经网络的project(你可以pip install kviz)。您应该能够针对不同的 NN 库调整此代码,而无需太多 进行太多更改。

            我想在这里添加的其他答案中没有看到的是不同数据点的激活动画:

            如果您有很多数据点并可视化训练之前(左)和之后(右)训练后的激活,您可以加快速度:

            所有这些都是通过animate_activations 方法here 完成的。这对我捕捉网络架构中的冗余、死神经元等非常有帮助。

            【讨论】:

              猜你喜欢
              • 2020-02-28
              • 2020-03-19
              • 2017-05-20
              • 2021-09-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-10-24
              相关资源
              最近更新 更多