Advertisement
cr1901

F95 Broken MLP Example

Apr 21st, 2015
313
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Fortran 16.35 KB | None | 0 0
  1. module mlp
  2.     !Passing allocatable arrays is only allowed within a module.
  3.     !Ditto with mixed privacy components of derived types.
  4.     !Alloctable components of derived types is a 95 feature.
  5.     !Look up- passing non-allocatable derived types with allocatable components.
  6.     !Alos look up- passing allocatable derived types with allocatable components.
  7.     !Only one part-reference is allowed to have nonzero rank when referencing data, for simplicity.
  8.     !Also, an array of pointers cannot be accessed in one statement.
  9.     implicit none
  10.    
  11.    
  12.     !These interfaces permit function overloading.
  13.     interface set_activation
  14.         module procedure pset_activation, ffset_activation
  15.     end interface
  16.    
  17.     interface set_beta
  18.         module procedure pset_beta, ffset_beta
  19.     end interface
  20.    
  21.    
  22.     !Constants- represent activation functions
  23.     integer, parameter :: threshold = 1, identity = 2, logistic = 3, hyperbolic = 4
  24.    
  25.    
  26.     !Discriminant function for individual perceptrons
  27.     type, public :: discriminant       
  28.         double precision, pointer :: weights(:) !Allocate data for this
  29.         double precision :: bias
  30.         integer, private :: act_func_type = logistic
  31.         double precision, private :: beta = 1.0D0
  32.     end type discriminant
  33.    
  34.     !Data structure of MLP nodes. Feedforward requires a doubly-linked
  35.     !list for backpropogation, unless I'm creative with pointers.
  36.     !Keep it simple for now. Id_no is for debugging.
  37.     !fb_next and fb_prev are for possible expansion.
  38.     type, public :: mlp_nodes
  39.         type(discriminant), private :: disc_func
  40.         type(mlp_nodes), private, pointer :: ff_next(:), ff_prev(:), fb_next(:), fb_prev(:)
  41.         double precision, private :: output
  42.         integer :: id_no
  43.     end type mlp_nodes
  44.    
  45.     !Training parameters structure.
  46.     type, public :: training_params
  47.         integer :: v_checks = 6
  48.         integer :: num_iterations = 1000
  49.         double precision :: eta = 1.0D-2
  50.         double precision :: alpha = 0.0
  51.         double precision :: threshold = 1.0D-6       
  52.         !double precision :: mu
  53.     end type training_params
  54.    
  55.     !Array of MLP layers.
  56.     !This data structure makes it easier to address all nodes in a
  57.     !single layer.
  58.     type, public :: mlp_layers
  59.         type(mlp_nodes), pointer :: curr_nodes(:)
  60.     end type mlp_layers
  61.    
  62.  
  63.  
  64.     !Begin function definitions
  65.     contains
  66.    
  67.         !Perceptron activation function
  68.         double precision function activation(disc, features)
  69.             double precision, intent(in) :: features(:)
  70.             type(discriminant), intent(in) :: disc
  71.            
  72.             double precision :: disc_result
  73.            
  74.             disc_result = dot_product((/ disc%weights, disc%bias /), (/ features, 1.0D0 /))        
  75.            
  76.             select case(disc%act_func_type)
  77.                 case(threshold)
  78.                     if(disc_result >= 0.0D0) then
  79.                         activation = 1.0D0
  80.                     else
  81.                         activation = 0.0D0
  82.                     end if             
  83.                 case(identity)
  84.                     activation = disc_result                   
  85.                 case(logistic)
  86.                     activation = (1.0D0/(1.0D0 + dexp(-1.0D0 * disc%beta * disc_result)))
  87.                 case(hyperbolic)
  88.                     activation = ((2.0D0/(1.0D0 + dexp(-1.0D0 * disc%beta * disc_result))) - 1.0D0)                        
  89.                 case default
  90.                     activation = 0.0D0
  91.             end select
  92.             return 
  93.         end function activation
  94.        
  95.        
  96. !Overloaded private variable set routines for perceptron/mlp.
  97.         subroutine pset_beta(disc, x)
  98.             type(discriminant), intent(inout) :: disc
  99.             double precision, intent(in) :: x
  100.             disc%beta = x
  101.         end subroutine
  102.        
  103.         subroutine pset_activation(disc, x)
  104.             type(discriminant), intent(inout) :: disc
  105.             integer, intent(in) :: x
  106.             disc%act_func_type = x
  107.         end subroutine
  108.        
  109.        
  110.         subroutine ffset_beta(ffnet, x)
  111.             type(mlp_layers), intent(inout) :: ffnet(:)
  112.             double precision, intent(in) :: x
  113.             integer :: i, j
  114.            
  115.             do i = 1, size(ffnet)
  116.                 do j = 1, size(ffnet(i)%curr_nodes)
  117.                     ffnet(i)%curr_nodes(j)%disc_func%beta = x
  118.                 end do
  119.             end do
  120.         end subroutine
  121.        
  122.         subroutine ffset_activation(ffnet, x)
  123.             type(mlp_layers), intent(inout) :: ffnet(:)
  124.             integer, intent(in) :: x
  125.             integer :: i, j
  126.            
  127.             do i = 1, size(ffnet)
  128.                 do j = 1, size(ffnet(i)%curr_nodes)
  129.                     ffnet(i)%curr_nodes(j)%disc_func%act_func_type = x
  130.                 end do
  131.             end do 
  132.         end subroutine
  133.        
  134.        
  135. !Feedforward Routines  
  136.         !FeedForward Allocation
  137.         !Allocate space for a feedforward MLP. Nodes are NOT connected.
  138.         subroutine ffalloc(layers, num_inputs, hl_array, num_outputs)
  139.             integer, intent(in) :: num_inputs, hl_array(:), num_outputs
  140.             type(mlp_layers), intent(out), allocatable :: layers(:)
  141.            
  142.             !Array indices in FORTRAN can be changed by using a pointer
  143.             !to specific indices. This makes my life slightly easier
  144.             !in the upcoming do-loop.
  145.             !integer, pointer ::  
  146.             integer :: num_hl, num_layers, i, j, k = 1
  147.            
  148.             num_hl = size(hl_array)
  149.             num_layers = num_hl+ 2
  150.            
  151.             !Allocate space for layer information
  152.             allocate(layers(1:num_layers))
  153.            
  154.            
  155.             !Then allocate space for each node (excluding weights).
  156.             !First layer
  157.             allocate(layers(1)%curr_nodes(num_inputs))
  158.            
  159.             !For the hidden layers
  160.             do i = 1, num_hl, 1
  161.                 allocate(layers(i + 1)%curr_nodes(hl_array(i)))
  162.             end do
  163.            
  164.             !Output layer
  165.             allocate(layers(num_layers)%curr_nodes(num_outputs))
  166.            
  167.            
  168.             !Then attach nodes to one another, and allocate space for
  169.             !weights.
  170.             do i = 1, num_layers, 1            
  171.                 !For each node in the current layer...
  172.                 do j = 1, size(layers(i)%curr_nodes), 1
  173.                     !print *, layers(i)%curr_nodes(j)%id_no
  174.                                
  175.                     layers(i)%curr_nodes(j)%id_no = k
  176.                    
  177.                     !Attach the nodes from the previous layer as inputs.
  178.                     if (i /= 1) then
  179.                         layers(i)%curr_nodes(j)%ff_prev => layers(i - 1)%curr_nodes
  180.                        
  181.                         !Weights are allocated here.
  182.                         allocate(layers(i)%curr_nodes(j)%disc_func%weights(size(layers(i - 1)%curr_nodes)))
  183.                     else
  184.                         nullify(layers(i)%curr_nodes(j)%ff_prev)
  185.                        
  186.                         !Weights for the first layer are still allocated, but not used.
  187.                         !(I put logic to skip the dot product in the first layer).
  188.                         allocate(layers(i)%curr_nodes(j)%disc_func%weights(1))
  189.                     end if
  190.                    
  191.                     !Also connect the next layer of nodes to the current
  192.                     !node's output.
  193.                     if (i /= num_layers) then
  194.                         layers(i)%curr_nodes(j)%ff_next => layers(i + 1)%curr_nodes
  195.                     else
  196.                         nullify(layers(i)%curr_nodes(j)%ff_next)
  197.                     end if
  198.                    
  199.                    
  200.                     !For future expansion.
  201.                     nullify(layers(i)%curr_nodes(j)%fb_next)
  202.                     nullify(layers(i)%curr_nodes(j)%fb_prev)
  203.                        
  204.                        
  205.                     k = k + 1
  206.                 end do                                             
  207.             end do
  208.         end subroutine ffalloc
  209.        
  210.        
  211.         !Deallocate memory from neural network
  212.         subroutine ffdealloc(layers)
  213.             type(mlp_layers), intent(inout), allocatable :: layers(:)
  214.             integer :: num_hl, num_layers, i, j, k, l = 1
  215.            
  216.             num_layers  = size(layers)
  217.             num_hl = num_hl - 2
  218.            
  219.            
  220.             do i = 1, num_layers, 1            
  221.                 !For each node in the current layer...
  222.                 do j = 1, size(layers(i)%curr_nodes), 1
  223.                
  224.                     !Deallocate it's weights...
  225.                     if(associated(layers(i)%curr_nodes(j)%disc_func%weights)) then
  226.                         deallocate(layers(i)%curr_nodes(j)%disc_func%weights)
  227.                     end if
  228.                    
  229.                     !And nullify it's pointers
  230.                    
  231.                     if(associated(layers(i)%curr_nodes(j)%ff_next)) then
  232.                         nullify(layers(i)%curr_nodes(j)%ff_next)
  233.                     end if
  234.                    
  235.                     if(associated(layers(i)%curr_nodes(j)%ff_prev)) then
  236.                         nullify(layers(i)%curr_nodes(j)%ff_prev)
  237.                     end if
  238.                    
  239.                     if(associated(layers(i)%curr_nodes(j)%fb_prev)) then
  240.                         nullify(layers(i)%curr_nodes(j)%fb_prev)
  241.                     end if
  242.                    
  243.                     if(associated(layers(i)%curr_nodes(j)%fb_next)) then
  244.                         nullify(layers(i)%curr_nodes(j)%fb_next)
  245.                     end if
  246.                    
  247.                     !Objects will be destroyed, so setting to null is
  248.                     !probably not necessary, but done as a precaution.
  249.                 end do                                             
  250.             end do
  251.            
  252.             !After all nodes pointers are deallocated, deallocate the nodes
  253.             !in each layer.
  254.             do k = 1, num_layers, 1
  255.                 if(associated(layers(k)%curr_nodes)) then
  256.                     deallocate(layers(k)%curr_nodes)
  257.                 end if
  258.             end do
  259.            
  260.             !Finally, deallocate the layers (which is an allocatable
  261.             !array as opposed to pointer- allocated pointers are also
  262.             !associated. Deallocated pointers are also nullified.      
  263.             if(allocated(layers)) then
  264.                 deallocate(layers)
  265.             end if
  266.         end subroutine ffdealloc
  267.    
  268.        
  269.         !Train feedforward neural network
  270.         subroutine fftrain(ffnet, inputs, validation, targets, params)
  271.             type(mlp_layers), intent(inout) :: ffnet(:)
  272.             double precision, intent(in) :: inputs(:,:)
  273.             double precision, intent(in) :: validation(:,:)
  274.             double precision, intent(in) :: targets(:,:)
  275.             type(training_params), intent(in) :: params
  276.            
  277.            
  278.             !double precision, allocatable :: validation(:,:)
  279.            
  280.             !Row- layer number, column- vector component.
  281.             double precision, pointer :: sensitivities(:,:)
  282.             integer :: i, j, k, seed
  283.            
  284.             write(*,'((F8.5,1X))')  inputs(2,:)
  285.            
  286.             call system_clock(seed)
  287.            
  288.             !Randomly initialize weights.
  289.             do i = 1, size(ffnet), 1
  290.            
  291.             end do
  292.            
  293.             return
  294.            
  295.            
  296.             !Batch gradient descent
  297.             !For the required number of iterations
  298.             !do i = 1:params%num_iterations
  299.                
  300.                
  301.                 !do j
  302.            
  303.            
  304.             !end do
  305.            
  306.            
  307.            
  308.            
  309.            
  310.        
  311.         end subroutine fftrain
  312.        
  313.        
  314.         !Classify using neural network
  315.         subroutine ffsim(ffnet, test_vector, output_vector)
  316.             type(mlp_layers), intent(inout) :: ffnet(:)
  317.             double precision, intent(in) :: test_vector(:)
  318.             double precision, intent(out) :: output_vector(:)
  319.             integer :: i, j, k, num_layers
  320.            
  321.             num_layers = size(ffnet)       
  322.            
  323.             ffnet(1)%curr_nodes(:)%output = test_vector
  324.             !print *, 'Input Values: ', ffnet(1)%curr_nodes(:)%output
  325.            
  326.             !Propogate the input to the output by traversing each layer,
  327.             !using the outputs of the previous layer as inputs to the next.
  328.             k = size(test_vector) + 1
  329.             do i = 2,num_layers,1
  330.                 do j = 1, size(ffnet(i)%curr_nodes), 1
  331.                     !print *, 'Input to node ', k, ': ', ffnet(i)%curr_nodes(j)%ff_prev(:)%output
  332.                     ffnet(i)%curr_nodes(j)%output = &
  333.                         activation(ffnet(i)%curr_nodes(j)%disc_func, &
  334.                         ffnet(i)%curr_nodes(j)%ff_prev(:)%output)
  335.                        
  336.                     k = k + 1          
  337.                 end do
  338.             end do
  339.            
  340.             output_vector = ffnet(num_layers)%curr_nodes(:)%output
  341.         end subroutine ffsim
  342.        
  343.        
  344.        
  345.            
  346.         !Load created MLP and associated weights from a file. For
  347.         !simplicity, reserves UNIT 11 for now.
  348.         subroutine ffrestore(ffnet, filename)
  349.             type(mlp_layers), allocatable, intent(out) :: ffnet(:)
  350.             character(len=*), intent(in) :: filename
  351.            
  352.             !Version constant
  353.             character(len=*), parameter :: version = 'V0.1'
  354.             integer :: unit_no = 11
  355.            
  356.             !Variables
  357.             integer :: num_hl, num_layers, i, j, k, current_node, act_fun, &
  358.                 num_weights_curr_node
  359.             integer, allocatable :: nodes_per_layer(:)
  360.             character(len=128) :: buffer
  361.             character(len=10) :: format_str
  362.             double precision :: beta
  363.            
  364.             open(unit = unit_no, file = filename)
  365.            
  366.             !Read header
  367.             read(unit_no, '(A10)'), buffer
  368.            
  369.             if(buffer(1:10) /= ('NNTXT ' // version)) then
  370.                 return
  371.             end if
  372.            
  373.             !Read NUM_HL
  374.             read(unit_no, '(/A8,I5.1)'), buffer, num_hl
  375.             if(buffer(1:8) /= ('NUM_HL: ')) then
  376.                 return
  377.             end if
  378.            
  379.             num_layers = num_hl + 2
  380.             allocate(nodes_per_layer(num_layers))
  381.            
  382.             !Read NUM_INPUTS
  383.             read(unit_no, '(A12,I5.1)'), buffer, nodes_per_layer(1)
  384.             if(buffer(1:12) /= ('NUM_INPUTS: ')) then
  385.                 return
  386.             end if
  387.            
  388.             !Read NUM_OUTPUTS
  389.             read(unit_no, '(A13,I5.1)'), buffer, nodes_per_layer(num_layers)
  390.             if(buffer(1:13) /= ('NUM_OUTPUTS: ')) then
  391.                 return
  392.             end if
  393.                        
  394.             write(format_str, '(I5.1)'), num_hl
  395.             read(unit_no, '(A11,' // format_str // '(I5.1))'), buffer, nodes_per_layer(2:(num_layers - 1))
  396.             if(buffer(1:11) /= ('HL_VECTOR: ')) then
  397.                 return
  398.             end if
  399.            
  400.             !Now allocate space, since sufficient information is available.
  401.             call ffalloc(ffnet, nodes_per_layer(1), &
  402.                 nodes_per_layer(2:(num_layers - 1)), nodes_per_layer(num_layers))
  403.            
  404.             !Connect/destroy connections to nodes (not necessary for MLP).
  405.             read(unit_no, '(/A12)'), buffer
  406.             if(buffer(1:12) /= ('CONNECTIONS: ')) then
  407.                 return
  408.             end if
  409.        
  410.             do i = 1,sum(nodes_per_layer(1:(num_layers - 1))),1
  411.                 read(unit_no, '()')
  412.             end do
  413.            
  414.             !Assign activation functions for nodes.
  415.             read(unit_no, '(/A11)'), buffer
  416.             if(buffer(1:11) /= ('ACTIVATION: ')) then
  417.                 return
  418.             end if
  419.            
  420.             k = 1 + nodes_per_layer(1)
  421.             do i = 2,num_layers,1
  422.                 do j = 1, size(ffnet(i)%curr_nodes), 1
  423.                     read(unit_no, '(I5.1,1X,I1,1X,D25.17)'), current_node, act_fun, beta
  424.                    
  425.                     !print *, current_node, act_fun, beta
  426.                                
  427.                     if(current_node /= k) then
  428.                         return
  429.                     end if
  430.                    
  431.                     call set_activation(ffnet(i)%curr_nodes(j)%disc_func, act_fun)
  432.                     call set_beta(ffnet(i)%curr_nodes(j)%disc_func, beta)
  433.                     !print *, ffnet(i)%curr_nodes(j)%disc_func%act_func_type
  434.                     k = k + 1
  435.                 end do
  436.             end do
  437.            
  438.             read(unit_no, '(/A9)'), buffer
  439.             if(buffer(1:9) /= ('WEIGHTS: ')) then
  440.                 return
  441.             end if
  442.            
  443.             !Assign weights
  444.             k = 1 + nodes_per_layer(1)
  445.             do i = 2,num_layers,1
  446.                 do j = 1, size(ffnet(i)%curr_nodes), 1
  447.                     num_weights_curr_node = size(ffnet(i)%curr_nodes(j)%ff_prev)
  448.                    
  449.                     print *, k
  450.                     write(format_str, '(I5.1)'), num_weights_curr_node
  451.                     read(unit_no, '(I5.1,' // format_str // '(D25.17, 1X))'), &
  452.                         current_node, ffnet(i)%curr_nodes(j)%disc_func%weights
  453.                                
  454.                     if(current_node /= k) then
  455.                         return
  456.                     end if
  457.                     k = k + 1
  458.                 end do
  459.             end do
  460.            
  461.            
  462.             read(unit_no, '(/A6)'), buffer
  463.             if(buffer(1:6) /= ('BIAS: ')) then
  464.                 return
  465.             end if
  466.            
  467.             !Assign bias
  468.             k = 1 + nodes_per_layer(1)
  469.             do i = 2,num_layers,1
  470.                 do j = 1, size(ffnet(i)%curr_nodes), 1
  471.                     num_weights_curr_node = size(ffnet(i)%curr_nodes(j)%ff_prev)
  472.                    
  473.                     !print *, k
  474.                     read(unit_no, '(I5.1, D25.17)'), &
  475.                         current_node, ffnet(i)%curr_nodes(j)%disc_func%bias
  476.                                
  477.                     if(current_node /= k) then
  478.                         return
  479.                     end if
  480.                    
  481.                     k = k + 1
  482.                 end do
  483.             end do     
  484.             close(unit_no)
  485.         end subroutine ffrestore
  486.        
  487.        
  488.         !Save created MLP and associated weights to a file for later
  489.         !use.
  490.         subroutine ffsave(ffnet, filename)
  491.             type(mlp_layers), allocatable, intent(in) :: ffnet
  492.             character(len=*), intent(in) :: filename
  493.        
  494.         end subroutine ffsave
  495.        
  496.        
  497.         !Debug information routines
  498.         subroutine ffdebug_node(ffnet)     
  499.             type(mlp_layers), intent(in) :: ffnet(:)
  500.                        
  501.             integer :: num_hl, num_layers, i, j, k
  502.            
  503.             num_layers = size(ffnet)
  504.             num_hl = num_layers - 2
  505.            
  506.             print *, 'Debugging Information- Node Structure'
  507.             print *, 'MLP Number Layers: ', num_layers
  508.    
  509.             do i = 1, size(ffnet), 1
  510.                 print *, 'Nodes in Layer ', i, ':', size(ffnet(i)%curr_nodes)
  511.             end do
  512.             print *, ''
  513.            
  514.            
  515.             do j = 1, size(ffnet(1)%curr_nodes), 1     
  516.                 print *, 'Input Layer, Node ', j, ' Assigned ID: ', ffnet(1)%curr_nodes(j)%id_no
  517.                 print *, 'Current node points to: ', ffnet(1)%curr_nodes(j)%ff_next(:)%id_no
  518.                 print *, ''
  519.             end do
  520.            
  521.            
  522.             do i = 1, num_hl, 1                                        
  523.                 !For each node in the current layer...
  524.                 do j = 1, size(ffnet(i + 1)%curr_nodes), 1
  525.                     !print *, layers(i)%curr_nodes(j)%id_no
  526.                
  527.                     !Attach the nodes from the previous layer as inputs.
  528.                     !This statement cannot be vectorized, as you cannot
  529.                     !assign an array of values to an array of pointers in a derived
  530.                     !type in F90.
  531.                     !do k = 1, size(layers(i - 1)%curr_nodes), 1
  532.                     print *, 'Layer ', i + 1, ', Node ', j, ' Assigned ID: ', ffnet(i + 1)%curr_nodes(j)%id_no
  533.                     print *, 'Current node points to: ', ffnet(i + 1)%curr_nodes(j)%ff_next(:)%id_no
  534.                     print *, 'Current node pointed to by: ', ffnet(i + 1)%curr_nodes(j)%ff_prev(:)%id_no
  535.                     print *, 'Weights: ', ffnet(i + 1)%curr_nodes(j)%disc_func%weights
  536.                     print *, 'Bias: ', ffnet(i + 1)%curr_nodes(j)%disc_func%bias
  537.                     print *, 'Activation: ', ffnet(i + 1)%curr_nodes(j)%disc_func%act_func_type
  538.                     print *, ''
  539.                     !end do
  540.                     !k = k + 1
  541.                 end do                                                 
  542.             end do
  543.            
  544.             do j = 1, size(ffnet(num_layers)%curr_nodes), 1    
  545.                 print *, 'Ouput Layer, Node ', j, ' Assigned ID: ', ffnet(num_layers)%curr_nodes(j)%id_no
  546.                 print *, 'Current node pointed to by: ', ffnet(num_layers)%curr_nodes(j)%ff_prev(:)%id_no
  547.                 print *, 'Weights: ', ffnet(num_layers)%curr_nodes(j)%disc_func%weights
  548.                 print *, 'Bias: ', ffnet(num_layers)%curr_nodes(j)%disc_func%bias
  549.                 print *, 'Activation: ', ffnet(num_layers)%curr_nodes(j)%disc_func%act_func_type
  550.                 print *, ''
  551.             end do     
  552.         end subroutine ffdebug_node
  553.  
  554. end module mlp
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement