.
 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | =item table | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | The contents of a file of the format that could be supplied to | 
| 86 |  |  |  |  |  |  | the C field. | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | =item input_names | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | A name for each dimension of the input vectors. | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =item map_dim_x | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =item map_dim_y | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | The dimensions of the feature map to create - defaults to a toy 19. | 
| 97 |  |  |  |  |  |  | (note: this is Perl indexing, starting at zero). | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =item epochs | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | Number of epochs to run for (see L). | 
| 102 |  |  |  |  |  |  | Minimum number is C<1>. | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | =item learning_rate | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | The initial learning rate. | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =item train_start | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | Reference to code to call at the begining of training. | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | =item epoch_start | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | Reference to code to call at the begining of every epoch | 
| 115 |  |  |  |  |  |  | (such as a colour calibration routine). | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =item epoch_end | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | Reference to code to call at the end of every epoch | 
| 120 |  |  |  |  |  |  | (such as a display routine). | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =item train_end | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | Reference to code to call at the end of training. | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | =item targeting | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | If undefined, random targets are chosen; otherwise | 
| 129 |  |  |  |  |  |  | they're iterated over. Just for experimental purposes. | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | =item smoothing | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | The amount of smoothing to apply by default when C | 
| 134 |  |  |  |  |  |  | is applied (see L). | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | =item neighbour_factor | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | When working out the size of the neighbourhood of influence, | 
| 139 |  |  |  |  |  |  | the average of the dimensions of the map are divided by this variable, | 
| 140 |  |  |  |  |  |  | before the exponential function is applied: the default value is 2.5, | 
| 141 |  |  |  |  |  |  | but you may with to use 2 or 4. | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | =item missing_mask | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | Used to signify data is missing in an input vector. Defaults | 
| 146 |  |  |  |  |  |  | to C. | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | =back | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | Private fields: | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =over 4 | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | =item time_constant | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | The number of iterations (epochs) to be completed, over the log of the map radius. | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | =item t | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | The current epoch, or moment in time. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =item l | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | The current learning rate. | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =item map_dim_a | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | Average of the map dimensions. | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | =back | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =cut | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | sub new { | 
| 175 | 4 |  |  | 4 | 0 | 4300 | my $class					= shift; | 
| 176 | 4 |  |  |  |  | 17 | my %args					= @_; | 
| 177 | 4 |  |  |  |  | 15 | my $self 					= bless \%args,$class; | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 4 | 50 |  |  |  | 24 | $self->{missing_mask}		= 'x' unless defined $self->{missing_mask}; | 
| 180 | 4 | 100 |  |  |  | 16 | $self->_process_table if defined $self->{table};	# Creates {input} | 
| 181 | 4 | 50 |  |  |  | 12 | $self->load_input($self->{input_file}) if defined $self->{input_file};	# Creates {input} | 
| 182 | 4 | 100 |  |  |  | 14 | if (not defined $self->{input}){ | 
| 183 | 1 |  |  |  |  | 228 | cluck "No {input} supplied!"; | 
| 184 | 1 |  |  |  |  | 16 | return undef; | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 3 | 100 |  |  |  | 11 | $self->{map_dim_x}			= 19 unless defined $self->{map_dim_x}; | 
| 188 | 3 | 100 |  |  |  | 12 | $self->{map_dim_y}			= 19 unless defined $self->{map_dim_y}; | 
| 189 |  |  |  |  |  |  | # Legacy from...yesterday | 
| 190 | 3 | 50 |  |  |  | 11 | if ($self->{map_dim}){ | 
| 191 | 0 |  |  |  |  | 0 | $self->{map_dim_x} 		= $self->{map_dim_y} = $self->{map_dim} | 
| 192 |  |  |  |  |  |  | } | 
| 193 | 3 | 50 | 33 |  |  | 42 | if (not defined $self->{map_dim_x} or $self->{map_dim_x}==0 | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 194 |  |  |  |  |  |  | or not defined $self->{map_dim_y} or $self->{map_dim_y}==0){ | 
| 195 | 0 |  |  |  |  | 0 | confess "No map dimensions in the input!"; | 
| 196 |  |  |  |  |  |  | } | 
| 197 | 3 | 100 |  |  |  | 603 | if ($self->{map_dim_x}>$self->{map_dim_y}){ | 
| 198 | 1 |  |  |  |  | 5 | $self->{map_dim_a} 		= $self->{map_dim_y} + (($self->{map_dim_x}-$self->{map_dim_y})/2) | 
| 199 |  |  |  |  |  |  | } else { | 
| 200 | 2 |  |  |  |  | 14 | $self->{map_dim_a} 		= $self->{map_dim_x} + (($self->{map_dim_y}-$self->{map_dim_x})/2) | 
| 201 |  |  |  |  |  |  | } | 
| 202 | 3 | 50 |  |  |  | 13 | $self->{neighbour_factor}	= 2.5 unless $self->{neighbour_factor}; | 
| 203 | 3 | 100 |  |  |  | 14 | $self->{epochs}				= 99 unless defined $self->{epochs}; | 
| 204 | 3 | 50 |  |  |  | 9 | $self->{epochs}				= 1 if $self->{epochs}<1; | 
| 205 | 3 | 50 |  |  |  | 49 | $self->{time_constant}		= $self->{epochs} / log($self->{map_dim_a}) unless $self->{time_constant};	# to base 10? | 
| 206 | 3 | 50 |  |  |  | 10 | $self->{learning_rate}		= 0.5 unless $self->{learning_rate}; | 
| 207 | 3 |  |  |  |  | 6 | $self->{l}					= $self->{learning_rate}; | 
| 208 | 3 | 50 |  |  |  | 9 | if (not $self->{weight_dim}){ | 
| 209 | 0 |  |  |  |  | 0 | cluck "{weight_dim} not set"; | 
| 210 | 0 |  |  |  |  | 0 | return undef; | 
| 211 |  |  |  |  |  |  | } | 
| 212 | 3 |  |  |  |  | 952 | $self->randomise_map; | 
| 213 | 3 |  |  |  |  | 17 | return $self; | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | =head1 METHOD randomise_map | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | Populates the C | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | See L. | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | =cut | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 3 |  |  | 3 | 0 | 7 | sub randomise_map { my $self=shift; | 
| 228 | 3 | 50 |  |  |  | 9 | confess "{weight_dim} not set" unless $self->{weight_dim}; | 
| 229 | 3 | 50 |  |  |  | 7 | confess "{map_dim_x} not set" unless $self->{map_dim_x}; | 
| 230 | 3 | 50 |  |  |  | 8 | confess "{map_dim_y} not set" unless $self->{map_dim_y}; | 
| 231 | 3 |  |  |  |  | 9 | for my $x (0..$self->{map_dim_x}){ | 
| 232 | 46 |  |  |  |  | 420 | $self->{map}->[$x] = []; | 
| 233 | 46 |  |  |  |  | 83 | for my $y (0..$self->{map_dim_y}){ | 
| 234 | 796 |  |  |  |  | 3290 | $self->{map}->[$x]->[$y] = new AI::NeuralNet::Kohonen::Node( | 
| 235 |  |  |  |  |  |  | dim => $self->{weight_dim}, | 
| 236 |  |  |  |  |  |  | missing_mask => $self->{missing_mask}, | 
| 237 |  |  |  |  |  |  | ); | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | =head1 METHOD clear_map | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | As L but sets all C | 
| 246 |  |  |  |  |  |  | either the value supplied as the only paramter, or C. | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | =cut | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 0 |  |  | 0 | 0 | 0 | sub clear_map { my $self=shift; | 
| 251 | 0 | 0 |  |  |  | 0 | confess "{weight_dim} not set" unless $self->{weight_dim}; | 
| 252 | 0 | 0 |  |  |  | 0 | confess "{map_dim_x} not set" unless $self->{map_dim_x}; | 
| 253 | 0 | 0 |  |  |  | 0 | confess "{map_dim_y} not set" unless $self->{map_dim_y}; | 
| 254 | 0 |  | 0 |  |  | 0 | my $val = shift || $self->{missing_mask}; | 
| 255 | 0 |  |  |  |  | 0 | my $w = []; | 
| 256 | 0 |  |  |  |  | 0 | foreach (0..$self->{weight_dim}){ | 
| 257 | 0 |  |  |  |  | 0 | push @$w, $val; | 
| 258 |  |  |  |  |  |  | } | 
| 259 | 0 |  |  |  |  | 0 | for my $x (0..$self->{map_dim_x}){ | 
| 260 | 0 |  |  |  |  | 0 | $self->{map}->[$x] = []; | 
| 261 | 0 |  |  |  |  | 0 | for my $y (0..$self->{map_dim_y}){ | 
| 262 | 0 |  |  |  |  | 0 | $self->{map}->[$x]->[$y] = new AI::NeuralNet::Kohonen::Node( | 
| 263 |  |  |  |  |  |  | weight		 => $w, | 
| 264 |  |  |  |  |  |  | dim 		 => $self->{weight_dim}, | 
| 265 |  |  |  |  |  |  | missing_mask => $self->{missing_mask}, | 
| 266 |  |  |  |  |  |  | ); | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  | } | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | =head1 METHOD train | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | Optionally accepts a parameter that is the number of epochs | 
| 277 |  |  |  |  |  |  | for which to train: the default is the value in the C field. | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | An epoch is composed of A number of generations, the number being | 
| 280 |  |  |  |  |  |  | the total number of input vectors. | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | For every generation, iterates: | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | =over 4 | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =item 1 | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | selects a target from the input array (see L); | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | =item 2 | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | finds the best-matching unit (see L); | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | =item 3 | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | adjusts the neighbours of the BMU (see L); | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | =back | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | At the end of every generation, the learning rate is decayed | 
| 301 |  |  |  |  |  |  | (see L). | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | See C for details of applicable callbacks. | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | Returns a true value. | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | =cut | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 1 |  |  | 1 | 0 | 3778 | sub train { my ($self,$epochs) = (shift,shift); | 
| 310 | 1 | 50 |  |  |  | 8 | $epochs = $self->{epochs} unless defined $epochs; | 
| 311 | 1 | 50 |  |  |  | 4 | &{$self->{train_start}} if exists $self->{train_start}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 312 | 1 |  |  |  |  | 4 | for my $epoch (1..$epochs){ | 
| 313 | 2 |  |  |  |  | 128 | $self->{t} = $epoch; | 
| 314 | 2 | 50 |  |  |  | 10 | &{$self->{epoch_start}} if exists $self->{epoch_start}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 315 | 2 |  |  |  |  | 5 | for (0..$#{$self->{input}}){ | 
|  | 2 |  |  |  |  | 8 |  | 
| 316 | 6 |  |  |  |  | 23 | my $target = $self->_select_target; | 
| 317 | 6 |  |  |  |  | 57 | my $bmu = $self->find_bmu($target); | 
| 318 | 6 |  |  |  |  | 33 | $self->_adjust_neighbours_of($bmu,$target); | 
| 319 |  |  |  |  |  |  | } | 
| 320 | 2 |  |  |  |  | 10 | $self->_decay_learning_rate; | 
| 321 | 2 | 50 |  |  |  | 24 | &{$self->{epoch_end}} if exists $self->{epoch_end}; | 
|  | 2 |  |  |  |  | 11 |  | 
| 322 |  |  |  |  |  |  | } | 
| 323 | 1 | 50 |  |  |  | 91 | &{$self->{train_end}} if $self->{train_end}; | 
|  | 1 |  |  |  |  | 5 |  | 
| 324 | 1 |  |  |  |  | 372 | return 1; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =head1 METHOD find_bmu | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | For a specific taraget, finds the Best Matching Unit in the map | 
| 331 |  |  |  |  |  |  | and return the x/y index. | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | Accepts: a reference to an array that is the target. | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | Returns: a reference to an array that is the BMU (and should | 
| 336 |  |  |  |  |  |  | perhaps be abstracted as an object in its own right), indexed as follows: | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | =over 4 | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | =item 0 | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | euclidean distance from the supplied target | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | =item 1, 2 | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | I and I co-ordinate in the map | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =back | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | See L, | 
| 351 |  |  |  |  |  |  | and L, | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =cut | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  |  | 
| 356 | 14 |  |  | 14 | 0 | 28 | sub find_bmu { my ($self,$target) = (shift,shift); | 
| 357 | 14 |  |  |  |  | 29 | my $closest = [];	# [value, x,y] value and co-ords of closest match | 
| 358 | 14 |  |  |  |  | 34 | for my $x (0..$self->{map_dim_x}){ | 
| 359 | 210 |  |  |  |  | 388 | for my $y (0..$self->{map_dim_y}){ | 
| 360 | 2310 |  |  |  |  | 8280 | my $distance = $self->{map}->[$x]->[$y]->distance_from( $target ); | 
| 361 | 2310 | 100 | 100 |  |  | 8028 | $closest = [$distance,0,0] if $x==0 and $y==0; | 
| 362 | 2310 | 100 |  |  |  | 6256 | $closest = [$distance,$x,$y] if $distance < $closest->[0]; | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  | } | 
| 365 | 14 |  |  |  |  | 44 | return $closest; | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | =head1 METHOD get_weight_at | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | Returns a reference to the weight array at the supplied I,I | 
| 371 |  |  |  |  |  |  | co-ordinates. | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | Accepts: I,I co-ordinates, each a scalar. | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | Returns: reference to an array that is the weight of the node, or | 
| 376 |  |  |  |  |  |  | C on failure. | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | =cut | 
| 379 |  |  |  |  |  |  |  | 
| 380 | 0 |  |  | 0 | 0 | 0 | sub get_weight_at { my ($self,$x,$y) = (shift,shift,shift); | 
| 381 | 0 | 0 | 0 |  |  | 0 | return undef if $x<0 or $y<0 or $x>$self->{map_dim_x} or $y>$self->{map_dim_y}; | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 382 | 0 |  |  |  |  | 0 | return $self->{map}->[$x]->[$y]->{weight}; | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | =head1 METHOD get_results | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | Finds and returns the results for all input vectors in the supplied | 
| 390 |  |  |  |  |  |  | reference to an array of arrays, | 
| 391 |  |  |  |  |  |  | placing the values in the C field (array reference), | 
| 392 |  |  |  |  |  |  | and, returning it either as an array or as it is, depending on | 
| 393 |  |  |  |  |  |  | the calling context | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | If no array reference of input vectors is supplied, will use | 
| 396 |  |  |  |  |  |  | the values in the C field. | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | Individual results are in the array format as described in | 
| 399 |  |  |  |  |  |  | L. | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | See L, and L. | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =cut | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 4 |  |  | 4 | 0 | 2992 | sub get_results { my ($self,$targets)=(shift,shift); | 
| 406 | 4 |  |  |  |  | 10 | $self->{results} = []; | 
| 407 | 4 | 100 |  |  |  | 28 | if (not defined $targets){ | 
|  |  | 50 |  |  |  |  |  | 
| 408 | 1 |  |  |  |  | 3 | $targets = $self->{input}; | 
| 409 |  |  |  |  |  |  | } elsif (not $targets eq $self->{input}){ | 
| 410 | 3 |  |  |  |  | 9 | foreach (@$targets){ | 
| 411 | 5 | 100 |  |  |  | 19 | next if ref $_ eq 'AI::NeuralNet::Kohonen::Input'; | 
| 412 | 4 |  |  |  |  | 18 | $_ = new AI::NeuralNet::Kohonen::Input(values=>$_); | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  | } | 
| 415 | 4 |  |  |  |  | 6 | foreach my $target (@{ $targets}){ | 
|  | 4 |  |  |  |  | 10 |  | 
| 416 | 8 |  |  |  |  | 24 | $_ = $self->find_bmu($target); | 
| 417 | 8 |  | 100 |  |  | 66 | push @$_, $target->{class}||"?"; | 
| 418 | 8 |  |  |  |  | 12 | push @{$self->{results}}, $_; | 
|  | 8 |  |  |  |  | 28 |  | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  | # Make it a scalar if it's a scalar | 
| 421 |  |  |  |  |  |  | #	if ($#{$self->{results}} == 0){ | 
| 422 |  |  |  |  |  |  | #		$self->{results} = @{$self->{results}}[0]; | 
| 423 |  |  |  |  |  |  | #	} | 
| 424 | 4 | 50 |  |  |  | 16 | return wantarray? @{$self->{results}} : $self->{results}; | 
|  | 4 |  |  |  |  | 29 |  | 
| 425 |  |  |  |  |  |  | } | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | =head1 METHOD map_results | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | Clears the C | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | The sole paramter is passed to the L. | 
| 433 |  |  |  |  |  |  | L is then called, and the results | 
| 434 |  |  |  |  |  |  | returned fed into the object field C | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | This may change, as it seems misleading to re-use that field. | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | =cut | 
| 439 |  |  |  |  |  |  |  | 
| 440 | 0 |  |  | 0 | 0 | 0 | sub map_results { my $self=shift; | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =head1 METHOD dump | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | Print the current weight values to the screen. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =cut | 
| 450 |  |  |  |  |  |  |  | 
| 451 | 0 |  |  | 0 | 0 | 0 | sub dump { my $self=shift; | 
| 452 | 0 |  |  |  |  | 0 | print "    "; | 
| 453 | 0 |  |  |  |  | 0 | for my $x (0..$self->{map_dim_x}){ | 
| 454 | 0 |  |  |  |  | 0 | printf ("  %02d ",$x); | 
| 455 |  |  |  |  |  |  | } | 
| 456 | 0 |  |  |  |  | 0 | print"\n","-"x107,"\n"; | 
| 457 | 0 |  |  |  |  | 0 | for my $x (0..$self->{map_dim_x}){ | 
| 458 | 0 |  |  |  |  | 0 | for my $w (0..$self->{weight_dim}){ | 
| 459 | 0 |  |  |  |  | 0 | printf ("%02d | ",$x); | 
| 460 | 0 |  |  |  |  | 0 | for my $y (0..$self->{map_dim_y}){ | 
| 461 | 0 |  |  |  |  | 0 | printf("%.2f ", $self->{map}->[$x]->[$y]->{weight}->[$w]); | 
| 462 |  |  |  |  |  |  | } | 
| 463 | 0 |  |  |  |  | 0 | print "\n"; | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 0 |  |  |  |  | 0 | print "\n"; | 
| 466 |  |  |  |  |  |  | } | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | =head1 METHOD smooth | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | Perform gaussian smoothing upon the map. | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | Accepts: the length of the side of the square gaussian mask to apply. | 
| 474 |  |  |  |  |  |  | If not supplied, uses the value in the field C; if that is | 
| 475 |  |  |  |  |  |  | empty, uses the square root of the average of the map dimensions | 
| 476 |  |  |  |  |  |  | (C). | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | Returns: a true value. | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | =cut | 
| 481 |  |  |  |  |  |  |  | 
| 482 | 0 |  |  | 0 | 0 | 0 | sub smooth { my ($self,$smooth) = (shift,shift); | 
| 483 | 0 | 0 | 0 |  |  | 0 | $smooth = $self->{smoothing} if not $smooth and defined $self->{smoothing}; | 
| 484 | 0 | 0 |  |  |  | 0 | return unless $smooth; | 
| 485 | 0 |  |  |  |  | 0 | $smooth = int( sqrt $self->{map_dim_a} ); | 
| 486 | 0 |  |  |  |  | 0 | my $mask = _make_gaussian_mask($smooth); | 
| 487 |  |  |  |  |  |  |  | 
| 488 |  |  |  |  |  |  | # For every weight at every point | 
| 489 | 0 |  |  |  |  | 0 | for my $x (0..$self->{map_dim_x}){ | 
| 490 | 0 |  |  |  |  | 0 | for my $y (0..$self->{map_dim_y}){ | 
| 491 | 0 |  |  |  |  | 0 | for my $w (0..$self->{weight_dim}){ | 
| 492 |  |  |  |  |  |  | # Apply the mask | 
| 493 | 0 |  |  |  |  | 0 | for my $mx (0..$smooth){ | 
| 494 | 0 |  |  |  |  | 0 | for my $my (0..$smooth){ | 
| 495 | 0 |  |  |  |  | 0 | $self->{map}->[$x]->[$y]->{weight}->[$w] *= $mask->[$mx]->[$my]; | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  | } | 
| 499 |  |  |  |  |  |  | } | 
| 500 |  |  |  |  |  |  | } | 
| 501 | 0 |  |  |  |  | 0 | return 1; | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | =head1 METHOD load_input | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | Loads a SOM_PAK-format file of input vectors. | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | This method is automatically accessed if the constructor is supplied | 
| 511 |  |  |  |  |  |  | with an C field. | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | Requires: a path to a file. | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | Returns C on failure. | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | See L. | 
| 518 |  |  |  |  |  |  |  | 
| 519 |  |  |  |  |  |  | =cut | 
| 520 |  |  |  |  |  |  |  | 
| 521 | 0 |  |  | 0 | 0 | 0 | sub load_input { my ($self,$path) = (shift,shift); | 
| 522 | 0 |  |  |  |  | 0 | local *IN; | 
| 523 | 0 | 0 |  |  |  | 0 | if (not open IN,$path){ | 
| 524 | 0 |  |  |  |  | 0 | warn "Could not open file <$path>: $!"; | 
| 525 | 0 |  |  |  |  | 0 | return undef; | 
| 526 |  |  |  |  |  |  | } | 
| 527 | 0 |  |  |  |  | 0 | @_ = ; | 
| 528 | 0 |  |  |  |  | 0 | close IN; | 
| 529 | 0 |  |  |  |  | 0 | $self->_process_input_text(\@_); | 
| 530 | 0 |  |  |  |  | 0 | return 1; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =head1 METHOD save_file | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | Saves the map file in I format (see L) | 
| 537 |  |  |  |  |  |  | at the path specified in the first argument. | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | Return C on failure, a true value on success. | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | =cut | 
| 542 |  |  |  |  |  |  |  | 
| 543 | 0 |  |  | 0 | 0 | 0 | sub save_file { my ($self,$path) = (shift,shift); | 
| 544 | 0 |  |  |  |  | 0 | local *OUT; | 
| 545 | 0 | 0 |  |  |  | 0 | if (not open OUT,">$path"){ | 
| 546 | 0 |  |  |  |  | 0 | warn "Could not open file for writing <$path>: $!"; | 
| 547 | 0 |  |  |  |  | 0 | return undef; | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  | #- Dimensionality of the vectors (integer, compulsory). | 
| 550 | 0 |  |  |  |  | 0 | print OUT ($self->{weight_dim}+1)," ";	# Perl indexing | 
| 551 |  |  |  |  |  |  | #- Topology type, either hexa or rect (string, optional, case-sensitive). | 
| 552 | 0 | 0 |  |  |  | 0 | if (not defined $self->{display}){ | 
| 553 | 0 |  |  |  |  | 0 | print OUT "rect "; | 
| 554 |  |  |  |  |  |  | } else { # $self->{display} eq 'hex' | 
| 555 | 0 |  |  |  |  | 0 | print OUT "hexa "; | 
| 556 |  |  |  |  |  |  | } | 
| 557 |  |  |  |  |  |  | #- Map dimension in x-direction (integer, optional). | 
| 558 | 0 |  |  |  |  | 0 | print OUT $self->{map_dim_x}." "; | 
| 559 |  |  |  |  |  |  | #- Map dimension in y-direction (integer, optional). | 
| 560 | 0 |  |  |  |  | 0 | print OUT $self->{map_dim_y}." "; | 
| 561 |  |  |  |  |  |  | #- Neighborhood type, either bubble or gaussian (string, optional, case-sen- sitive). | 
| 562 | 0 |  |  |  |  | 0 | print OUT "gaussian "; | 
| 563 |  |  |  |  |  |  | # End of header | 
| 564 | 0 |  |  |  |  | 0 | print OUT "\n"; | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | # Format input data | 
| 567 | 0 |  |  |  |  | 0 | foreach (@{$self->{input}}){ | 
|  | 0 |  |  |  |  | 0 |  | 
| 568 | 0 |  |  |  |  | 0 | print OUT join("\t",@{$_->{values}}); | 
|  | 0 |  |  |  |  | 0 |  | 
| 569 | 0 | 0 |  |  |  | 0 | if ($_->{class}){ | 
| 570 | 0 |  |  |  |  | 0 | print OUT " $_->{class} " ; | 
| 571 |  |  |  |  |  |  | } | 
| 572 | 0 |  |  |  |  | 0 | print OUT "\n"; | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  | # EOF | 
| 575 | 0 |  |  |  |  | 0 | print OUT chr 26; | 
| 576 | 0 |  |  |  |  | 0 | close OUT; | 
| 577 | 0 |  |  |  |  | 0 | return 1; | 
| 578 |  |  |  |  |  |  | } | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | # | 
| 582 |  |  |  |  |  |  | # Process ASCII from table field or input file | 
| 583 |  |  |  |  |  |  | # Accepts: ASCII as array or array ref | 
| 584 |  |  |  |  |  |  | # | 
| 585 | 1 |  |  | 1 |  | 3 | sub _process_input_text { my ($self) = (shift); | 
| 586 | 1 | 50 |  |  |  | 4 | if (not defined $_[1]){ | 
| 587 | 1 | 50 |  |  |  | 4 | if (ref $_[0] eq 'ARRAY'){ | 
| 588 | 0 |  |  |  |  | 0 | @_ = @{$_[0]}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 589 |  |  |  |  |  |  | } else { | 
| 590 | 1 |  |  |  |  | 10 | @_ = split/[\n\r\f]+/,$_[0]; | 
| 591 |  |  |  |  |  |  | } | 
| 592 |  |  |  |  |  |  | } | 
| 593 | 1 |  |  |  |  | 4 | chomp @_; | 
| 594 | 1 |  |  |  |  | 5 | my @specs = split/\s+/,(shift @_); | 
| 595 |  |  |  |  |  |  | #- Dimensionality of the vectors (integer, compulsory). | 
| 596 | 1 |  |  |  |  | 4 | $self->{weight_dim} = shift @specs; | 
| 597 | 1 |  |  |  |  | 3 | $self->{weight_dim}--; # Perl indexing | 
| 598 |  |  |  |  |  |  | #- Topology type, either hexa or rect (string, optional, case-sensitive). | 
| 599 | 1 |  |  |  |  | 2 | my $display		    = shift @specs; | 
| 600 | 1 | 50 | 33 |  |  | 12 | if (not defined $display and exists $self->{display}){ | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | # Intentionally blank | 
| 602 |  |  |  |  |  |  | } elsif (not defined $display){ | 
| 603 | 1 |  |  |  |  | 4 | $self->{display} = undef; | 
| 604 |  |  |  |  |  |  | } elsif ($display eq 'hexa'){ | 
| 605 | 0 |  |  |  |  | 0 | $self->{display} = 'hex' | 
| 606 |  |  |  |  |  |  | } elsif ($display eq 'rect'){ | 
| 607 | 0 |  |  |  |  | 0 | $self->{display} = undef; | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  | #- Map dimension in x-direction (integer, optional). | 
| 610 | 1 |  |  |  |  | 3 | $_				      = shift @specs; | 
| 611 | 1 | 50 |  |  |  | 4 | $self->{map_dim_x}    = $_ if defined $_; | 
| 612 |  |  |  |  |  |  | #- Map dimension in y-direction (integer, optional). | 
| 613 | 1 |  |  |  |  | 1 | $_				      = shift @specs; | 
| 614 | 1 | 50 |  |  |  | 4 | $self->{map_dim_y}    = $_ if defined $_; | 
| 615 |  |  |  |  |  |  | #- Neighborhood type, either bubble or gaussian (string, optional, case-sen- sitive). | 
| 616 |  |  |  |  |  |  | # not implimented | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | # Format input data | 
| 619 | 1 |  |  |  |  | 3 | foreach (@_){ | 
| 620 | 3 |  |  |  |  | 8 | $self->_add_input_from_str($_); | 
| 621 |  |  |  |  |  |  | } | 
| 622 | 1 |  |  |  |  | 4 | return 1; | 
| 623 |  |  |  |  |  |  | } | 
| 624 |  |  |  |  |  |  |  | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | =head1 PRIVATE METHOD _select_target | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | Return a random target from the training set in the C field, | 
| 629 |  |  |  |  |  |  | unless the C field is defined, when the targets are | 
| 630 |  |  |  |  |  |  | iterated over. | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | =cut | 
| 633 |  |  |  |  |  |  |  | 
| 634 | 6 |  |  | 6 |  | 11 | sub _select_target { my $self=shift; | 
| 635 | 6 | 50 |  |  |  | 62 | if (not $self->{targeting}){ | 
| 636 | 6 |  |  |  |  | 31 | return $self->{input}->[ | 
| 637 | 6 |  |  |  |  | 12 | (int rand(scalar @{$self->{input}})) | 
| 638 |  |  |  |  |  |  | ]; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  | else { | 
| 641 | 0 |  |  |  |  | 0 | $self->{tar}++; | 
| 642 | 0 | 0 |  |  |  | 0 | if ($self->{tar}>$#{ $self->{input} }){ | 
|  | 0 |  |  |  |  | 0 |  | 
| 643 | 0 |  |  |  |  | 0 | $self->{tar} = 0; | 
| 644 |  |  |  |  |  |  | } | 
| 645 | 0 |  |  |  |  | 0 | return $self->{input}->[$self->{tar}]; | 
| 646 |  |  |  |  |  |  | } | 
| 647 |  |  |  |  |  |  | } | 
| 648 |  |  |  |  |  |  |  | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | =head1 PRIVATE METHOD _adjust_neighbours_of | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | Accepts: a reference to an array containing | 
| 653 |  |  |  |  |  |  | the distance of the BMU from the target, as well | 
| 654 |  |  |  |  |  |  | as the x and y co-ordinates of the BMU in the map; | 
| 655 |  |  |  |  |  |  | a reference to the target, which is an | 
| 656 |  |  |  |  |  |  | C object. | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | Returns: true. | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | =head2 FINDING THE NEIGHBOURS OF THE BMU | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | (      t   ) | 
| 663 |  |  |  |  |  |  | sigma(t) = sigma(0) exp ( - ------ ) | 
| 664 |  |  |  |  |  |  | (   lambda ) | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | Where C is the width of the map at any stage | 
| 667 |  |  |  |  |  |  | in time (C), and C is a time constant. | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | Lambda is our field C. | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | The map radius is naturally just half the map width. | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | =head2 ADJUSTING THE NEIGHBOURS OF THE BMU | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | W(t+1) = W(t) + THETA(t) L(t)( V(t)-W(t) ) | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | Where C is the learning rate, C the target vector, | 
| 678 |  |  |  |  |  |  | and C the weight. THETA(t) represents the influence | 
| 679 |  |  |  |  |  |  | of distance from the BMU upon a node's learning, and | 
| 680 |  |  |  |  |  |  | is calculated by the C class - see | 
| 681 |  |  |  |  |  |  | L. | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | =cut | 
| 684 |  |  |  |  |  |  |  | 
| 685 | 6 |  |  | 6 |  | 14 | sub _adjust_neighbours_of { my ($self,$bmu,$target) = (shift,shift,shift); | 
| 686 | 6 |  |  |  |  | 71 | my $neighbour_radius = int ( | 
| 687 |  |  |  |  |  |  | ($self->{map_dim_a}/$self->{neighbour_factor}) * exp(- $self->{t} / $self->{time_constant}) | 
| 688 |  |  |  |  |  |  | ); | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | # Distance from co-ord vector (0,0) as integer | 
| 691 |  |  |  |  |  |  | # Basically map_width * y  +  x | 
| 692 | 6 |  |  |  |  | 20 | my $centre = ($self->{map_dim_a}*$bmu->[2])+$bmu->[1]; | 
| 693 |  |  |  |  |  |  | # Set the class of the BMU | 
| 694 | 6 |  |  |  |  | 30 | $self->{map}->[ $bmu->[1] ]->[ $bmu->[2] ]->{class} = $target->{class}; | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 6 |  |  |  |  | 22 | for my $x ($bmu->[1]-$neighbour_radius .. $bmu->[1]+$neighbour_radius){ | 
| 697 | 12 | 50 | 33 |  |  | 67 | next if $x<0 or $x>$self->{map_dim_x};		# Ignore those not mappable | 
| 698 | 12 |  |  |  |  | 32 | for my $y ($bmu->[2]-$neighbour_radius .. $bmu->[2]+$neighbour_radius){ | 
| 699 | 30 | 50 | 33 |  |  | 174 | next if $y<0 or $y>$self->{map_dim_y};	# Ignore those not mappable | 
| 700 |  |  |  |  |  |  | # Skip node if it is out of the circle of influence | 
| 701 |  |  |  |  |  |  | next if ( | 
| 702 | 30 | 100 |  |  |  | 127 | (($bmu->[1] - $x) * ($bmu->[1] - $x)) + (($bmu->[2] - $y) * ($bmu->[2] - $y)) | 
| 703 |  |  |  |  |  |  | ) > ($neighbour_radius * $neighbour_radius); | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | # Adjust the weight | 
| 706 | 18 |  |  |  |  | 36 | for my $w (0..$self->{weight_dim}){ | 
| 707 | 54 | 50 |  |  |  | 293 | next if $target->{values}->[$w] eq $self->{map}->[$x]->[$y]->{missing_mask}; | 
| 708 | 54 |  |  |  |  | 99 | my $weight = \$self->{map}->[$x]->[$y]->{weight}->[$w]; | 
| 709 | 54 |  |  |  |  | 250 | $$weight = $$weight + ( | 
| 710 |  |  |  |  |  |  | $self->{map}->[$x]->[$y]->distance_effect($bmu->[0], $neighbour_radius) | 
| 711 |  |  |  |  |  |  | * ( $self->{l} * ($target->{values}->[$w] - $$weight) ) | 
| 712 |  |  |  |  |  |  | ); | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | } | 
| 715 |  |  |  |  |  |  | } | 
| 716 |  |  |  |  |  |  | } | 
| 717 |  |  |  |  |  |  |  | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | =head1 PRIVATE METHOD _decay_learning_rate | 
| 720 |  |  |  |  |  |  |  | 
| 721 |  |  |  |  |  |  | Performs a gaussian decay upon the learning rate (our C field). | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | (       t   ) | 
| 724 |  |  |  |  |  |  | L(t) = L  exp ( -  ------ ) | 
| 725 |  |  |  |  |  |  | 0     (    lambda ) | 
| 726 |  |  |  |  |  |  |  | 
| 727 |  |  |  |  |  |  | =cut | 
| 728 |  |  |  |  |  |  |  | 
| 729 | 2 |  |  | 2 |  | 5 | sub _decay_learning_rate { my $self=shift; | 
| 730 | 2 |  |  |  |  | 9 | $self->{l} =  ( | 
| 731 |  |  |  |  |  |  | $self->{learning_rate} * exp(- $self->{t} / $self->{time_constant}) | 
| 732 |  |  |  |  |  |  | ); | 
| 733 |  |  |  |  |  |  | } | 
| 734 |  |  |  |  |  |  |  | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | =head1 PRIVATE FUNCTION _make_gaussian_mask | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | Accepts: size of mask. | 
| 739 |  |  |  |  |  |  |  | 
| 740 |  |  |  |  |  |  | Returns: reference to a 2d array that is the mask. | 
| 741 |  |  |  |  |  |  |  | 
| 742 |  |  |  |  |  |  | =cut | 
| 743 |  |  |  |  |  |  |  | 
| 744 | 0 |  |  | 0 |  | 0 | sub _make_gaussian_mask { my ($smooth) = (shift); | 
| 745 | 0 |  |  |  |  | 0 | my $f = 4; # Cut-off threshold | 
| 746 | 0 |  |  |  |  | 0 | my $g_mask_2d = []; | 
| 747 | 0 |  |  |  |  | 0 | for my $x (0..$smooth){ | 
| 748 | 0 |  |  |  |  | 0 | $g_mask_2d->[$x] = []; | 
| 749 | 0 |  |  |  |  | 0 | for my $y (0..$smooth){ | 
| 750 | 0 |  |  |  |  | 0 | $g_mask_2d->[$x]->[$y] = | 
| 751 |  |  |  |  |  |  | _gauss_weight( $x-($smooth/2), $smooth/$f) | 
| 752 |  |  |  |  |  |  | * _gauss_weight( $y-($smooth/2), $smooth/$f ); | 
| 753 |  |  |  |  |  |  | } | 
| 754 |  |  |  |  |  |  | } | 
| 755 | 0 |  |  |  |  | 0 | return $g_mask_2d; | 
| 756 |  |  |  |  |  |  | } | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | =head1 PRIVATE FUNCTION _gauss_weight | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | Accepts: two paramters: the first, C, gives the distance from the mask centre, | 
| 761 |  |  |  |  |  |  | the second, C, specifies the width of the mask. | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | Returns the gaussian weight. | 
| 764 |  |  |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | See also L<_decay_learning_rate>. | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | =cut | 
| 768 |  |  |  |  |  |  |  | 
| 769 | 0 |  |  | 0 |  | 0 | sub _gauss_weight { my ($r, $sigma) = (shift,shift); | 
| 770 | 0 |  |  |  |  | 0 | return exp( -($r**2) / (2 * $sigma**2) ); | 
| 771 |  |  |  |  |  |  | } | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =head1 PUBLIC METHOD quantise_error | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | Returns the quantise error for either the supplied points, | 
| 777 |  |  |  |  |  |  | or those in the C field. | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | =cut | 
| 780 |  |  |  |  |  |  |  | 
| 781 |  |  |  |  |  |  |  | 
| 782 | 1 |  |  | 1 | 0 | 30 | sub quantise_error { my ($self,$targets) = (shift,shift); | 
| 783 | 1 |  |  |  |  | 3 | my $qerror=0; | 
| 784 | 1 | 50 |  |  |  | 6 | if (not defined $targets){ | 
| 785 | 0 |  |  |  |  | 0 | $targets = $self->{input}; | 
| 786 |  |  |  |  |  |  | } else { | 
| 787 | 1 |  |  |  |  | 4 | foreach (@$targets){ | 
| 788 | 1 | 50 | 33 |  |  | 13 | if (not ref $_ or ref $_ ne 'ARRAY'){ | 
| 789 | 0 |  |  |  |  | 0 | croak "Supplied target parameter should be an array of arrays!" | 
| 790 |  |  |  |  |  |  | } | 
| 791 | 1 |  |  |  |  | 10 | $_ = new AI::NeuralNet::Kohonen::Input(values=>$_); | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  | } | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | # Recieves an array of ONE element, | 
| 796 |  |  |  |  |  |  | # should be an array of an array of elements | 
| 797 | 1 |  |  |  |  | 5 | my @bmu = $self->get_results($targets); | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | # Check input and output dims are the same | 
| 800 | 1 | 50 |  |  |  | 3 | if ($#{$self->{map}->[0]->[1]->{weight}} != $targets->[0]->{dim}){ | 
|  | 1 |  |  |  |  | 8 |  | 
| 801 | 0 |  |  |  |  | 0 | confess "target input and map dimensions differ"; | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 | 1 |  |  |  |  | 4 | for my $i (0..$#bmu){ | 
| 805 | 1 |  |  |  |  | 5 | foreach my $w (0..$self->{weight_dim}){ | 
| 806 | 3 |  |  |  |  | 14 | $qerror += $targets->[$i]->{values}->[$w] | 
| 807 |  |  |  |  |  |  | - $self->{map}->[$bmu[$i]->[1]]->[$bmu[$i]->[2]]->{weight}->[$w]; | 
| 808 |  |  |  |  |  |  | } | 
| 809 |  |  |  |  |  |  | } | 
| 810 | 1 |  |  |  |  | 4 | $qerror /= scalar @$targets; | 
| 811 | 1 |  |  |  |  | 8 | return $qerror; | 
| 812 |  |  |  |  |  |  | } | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  |  | 
| 815 |  |  |  |  |  |  | =head1 PRIVATE METHOD _add_input_from_str | 
| 816 |  |  |  |  |  |  |  | 
| 817 |  |  |  |  |  |  | Adds to the C field an input vector in SOM_PAK-format | 
| 818 |  |  |  |  |  |  | whitespace-delimited ASCII. | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | Returns C on failure to add an item (perhaps because | 
| 821 |  |  |  |  |  |  | the data passed was a comment, or the C flag was | 
| 822 |  |  |  |  |  |  | not set); a true value on success. | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | =cut | 
| 825 |  |  |  |  |  |  |  | 
| 826 | 3 |  |  | 3 |  | 5 | sub _add_input_from_str { my ($self) = (shift); | 
| 827 | 3 |  |  |  |  | 4 | $_ = shift; | 
| 828 | 3 |  |  |  |  | 4 | s/#.*$//g; | 
| 829 | 3 | 50 | 33 |  |  | 19 | return undef if /^$/ or not defined $self->{weight_dim}; | 
| 830 | 3 |  |  |  |  | 11 | my @i = split /\s+/,$_; | 
| 831 | 3 | 50 |  |  |  | 10 | return undef if $#i < $self->{weight_dim}; # catch bad lines | 
| 832 |  |  |  |  |  |  | # 'x' in files signifies unknown: we prefer undef? | 
| 833 |  |  |  |  |  |  | #	@i[0..$self->{weight_dim}] = map{ | 
| 834 |  |  |  |  |  |  | #		$_ eq 'x'? undef:$_ | 
| 835 |  |  |  |  |  |  | #	} @i[0..$self->{weight_dim}]; | 
| 836 | 3 |  |  |  |  | 16 | my %args = ( | 
| 837 |  |  |  |  |  |  | dim 	=> $self->{weight_dim}, | 
| 838 |  |  |  |  |  |  | values	=> [ @i[0..$self->{weight_dim}] ], | 
| 839 |  |  |  |  |  |  | ); | 
| 840 | 3 | 50 |  |  |  | 14 | $args{class} = $i[$self->{weight_dim}+1] if $i[$self->{weight_dim}+1]; | 
| 841 | 3 | 50 |  |  |  | 9 | $args{enhance} = $i[$self->{weight_dim}+1] if $i[$self->{weight_dim}+2]; | 
| 842 | 3 | 50 |  |  |  | 8 | $args{fixed} = $i[$self->{weight_dim}+1] if $i[$self->{weight_dim}+3]; | 
| 843 | 3 |  |  |  |  | 4 | push @{$self->{input}}, new AI::NeuralNet::Kohonen::Input(%args); | 
|  | 3 |  |  |  |  | 17 |  | 
| 844 |  |  |  |  |  |  |  | 
| 845 | 3 |  |  |  |  | 12 | return 1; | 
| 846 |  |  |  |  |  |  | } | 
| 847 |  |  |  |  |  |  |  | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | # | 
| 850 |  |  |  |  |  |  | # Processes the 'table' paramter to the constructor | 
| 851 |  |  |  |  |  |  | # | 
| 852 | 1 |  |  | 1 |  | 2 | sub _process_table { my $self = shift; | 
| 853 | 1 |  |  |  |  | 6 | $_ = $self->_process_input_text( $self->{table} ); | 
| 854 | 1 |  |  |  |  | 3 | undef $self->{table}; | 
| 855 | 1 |  |  |  |  | 2 | return $_; | 
| 856 |  |  |  |  |  |  | } | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  |  | 
| 859 |  |  |  |  |  |  | __END__ |