Changeset 40319 for trunk/Ohana/src/opihi/cmd.data/nnet_train.c
- Timestamp:
- Jan 23, 2018, 10:11:50 AM (9 years ago)
- File:
-
- 1 edited
-
trunk/Ohana/src/opihi/cmd.data/nnet_train.c (modified) (12 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Ohana/src/opihi/cmd.data/nnet_train.c
r40318 r40319 1 1 # include "data.h" 2 void sortseq (float *X, int *Y, int N); 2 3 // these are local only 4 float nnet_sigmoid (float value); 5 void nnet_sortseq (float *X, int *Y, int N); 6 int nnet_onelayer (Nnet *nnet, int L); 7 void nnet_reset_Nabla (Nnet *nnet); 8 void nnet_update_Nabla (Nnet *nnet); 9 void nnet_apply_Nabla (Nnet *nnet, int Nmini, float eta); 10 void nnet_backprop (Nnet *nnet, Vector **inVec, Vector **outVec, int N); 11 void nnet_descent_step (Nnet *nnet, Vector **inVec, Vector **outVec, int *seq, int pass, int Nmini, float eta); 3 12 4 13 int nnet_train (int argc, char **argv) { 14 15 int N; 5 16 6 17 int Nepoch = 10; … … 18 29 } 19 30 31 float eta = 1.0; // XXX how do I set this? does it need to update? 32 20 33 if (argc < 4) { 21 34 gprint (GP_ERR, "USAGE: nnet train (nnet) [input] [input] ... [output] [output] ...\n"); … … 97 110 // generate a random sequence : used to select random mini batches 98 111 for (int i = 0; i < Ntrial; i++) { seq[i] = i; rnd[i] = drand48(); } 99 sortseq (rnd, seq, Ntrial);112 nnet_sortseq (rnd, seq, Ntrial); 100 113 101 114 int Npass = Ntrial / Nmini; … … 116 129 117 130 // this recreates 'update_mini_batch' from http://neuralnetworksanddeeplearning.com/chap1.html 118 void nnet_descent_step (N Net *nnet, Vector **inVec, Vector **outVec, int *seq, int pass, int Nmini, float eta) {131 void nnet_descent_step (Nnet *nnet, Vector **inVec, Vector **outVec, int *seq, int pass, int Nmini, float eta) { 119 132 120 133 int Ntrial = inVec[0][0].Nelements; … … 122 135 nnet_reset_Nabla (nnet); 123 136 124 for (i = 0; (i < Nmini) && (pass*Nmini + i < Ntrial); i++) {137 for (int i = 0; (i < Nmini) && (pass*Nmini + i < Ntrial); i++) { 125 138 126 139 // N is the element of the mini batch on which we are currently operating 127 N = seq[pass*Nmini + i];140 int N = seq[pass*Nmini + i]; 128 141 129 142 // backprop generates a dNabla_b, dNabla_w pair for the element N of the input and output vectors … … 136 149 } 137 150 138 void nnet_backprop (N Net *nnet, Vector **inVec, Vector **outVec, int N) {151 void nnet_backprop (Nnet *nnet, Vector **inVec, Vector **outVec, int N) { 139 152 140 153 // start with the input values 141 142 // store the input values for this row (trial) in the input value vector143 154 int Nlayer = nnet[0].Nlayer; 144 int Ninput = nnet[0].Nnodes[0]; 145 int Noutput = nnet[0].Nnodes[Nlayer - 1]; // number of input + output nodes 146 147 for (int j = 0; j < Ninput; j++) { 155 156 // store the input values for this row (trial) in the input vector "svalue[0]" 157 for (int j = 0; j < nnet[0].Nnodes[0]; j++) { 148 158 nnet[0].svalue[0][j] = inVec[j][0].elements.Flt[N]; 149 159 } … … 153 163 // activation = sigmoid (z) 154 164 165 // feedforward operates on the vector saved in svalue[0] 166 // feedforward saves the zvalues [w * input + bias], svalues [sigmoid(z)], sprimes [sigmoid'(z)] as it runs 155 167 nnet_feedforward (nnet); 156 168 157 // backward pass (note for now these are vector operations: 158 for (L = Nlayer - 1; L > 0; L--) { 159 160 // sp is array of same size as zvalue for each layer 161 // sp = sigmoid_prime (nnet[0].zvalue[L]); sprime is precalculated in feedforward 169 // backward pass 170 for (int L = Nlayer - 1; L > 0; L--) { 162 171 163 172 if (L == Nlayer - 1) { 164 173 // starting point uses cost_derivative to compare last svalue set with truth output 165 // delta = cost_derivative( nnet[0].svalue[L], outVec, N) * sprime[L];174 // delta = cost_derivative(svalue[L], output) * sprime[L]; 166 175 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 167 nnet[0].delta[L][j] = cost_derivative(nnet[0].svalue[L][j], outVec[j][0].elements.Flt[N]) * nnet[0].sprime[L][j]; 176 // cost_derivative (svalue[L][j], output[j]) = svalue[L][j] - output[j] 177 // (because cost = (1/2)(svalue[L][j] - output[j])^2) 178 // NOTE: the (1/Npt) factor is pushed to the apply_Nabla step 179 // nnet[0].delta[L][j] = cost_derivative(nnet[0].svalue[L][j], outVec[j][0].elements.Flt[N]) * nnet[0].sprime[L][j]; 180 nnet[0].delta[L][j] = (nnet[0].svalue[L][j] - outVec[j][0].elements.Flt[N]) * nnet[0].sprime[L][j]; 168 181 } 169 182 } else { 170 // delta = (delta DOT transpose(weights[L+1])) * sp;183 // delta = DOT(delta, transpose(weight[L+1])) * sprime; 171 184 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 172 tmpdelta =0;185 float tmpdelta = 0.0; 173 186 for (int i = 0; i < nnet[0].Nnodes[L+1]; i++) { 174 int k = j + i*nnet[0].Nnodes[L]; 175 tmpdelta += nnet[0].weight[L][k] * nnet[0].delta[L+1][i]; // XXX check on the index values187 int k = j + i*nnet[0].Nnodes[L]; // note order of (i,j) : j is [L+1] direction 188 tmpdelta += nnet[0].weight[L][k] * nnet[0].delta[L+1][i]; 176 189 } 177 190 nnet[0].delta[L][j] = tmpdelta * nnet[0].sprime[L][j]; … … 179 192 } 180 193 181 // UPDATE 182 Nabla_b[L] = delta; 194 // NOTE on weight array: for a given layer, L, the matrix weight[L] maps the nodes in 195 // the previous layer (L-1) to those in L: Nnodes[L-1] -> Nnodes[L]. This is a matrix 196 // with dimensions (Nnodes[L-1] x Nnodes[L]). The Nnodes[L-1] is the fast dimension, 197 // so for an element (i,j), the index k = i + j*Nnodes[L-1]. 198 199 // Nabla_b[L] = delta; 200 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 201 nnet[0]. Nabla_b[L][j] = nnet[0].delta[L][j]; 202 } 183 203 184 // Nabla_w[L] = delta DOT transpose(nnet[0].svalue[L-1]);204 // Nabla_w[L] = DOT(delta, transpose(svalue[L-1])); 185 205 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 186 206 for (int i = 0; i < nnet[0].Nnodes[L-1]; i++) { 187 int k = i + j*nnet[0].Nnodes[n-1]; 188 nnet[0]. Nabla_w[n][k] = nnet[0].svalue[L-1][i] * nnet[0].delta[L][j]; 189 } 190 } 191 207 int k = i + j*nnet[0].Nnodes[L-1]; 208 nnet[0]. Nabla_w[L][k] = nnet[0].svalue[L-1][i] * nnet[0].delta[L][j]; 209 } 210 } 192 211 } 193 212 } 194 213 195 214 // support functions to loop over the Nabla entries 196 void nnet_apply_Nabla (N Net *nnet, int Nmini, float eta) {197 for (int n = 1; n < nnet[0].Nlayer; n++) {198 199 for (int j = 0; j < nnet[0].Nnodes[ n]; j++) {200 nnet[0].biases[ n][j] -= (eta / Nmini) * nnet[0].Nabla_b[n][j];201 202 for (int i = 0; i < nnet[0].Nnodes[ n-1]; i++) {203 int k = i + j*nnet[0].Nnodes[ n-1];204 nnet[0].weight s[n][k] -= (eta / Nmini) * nnet[0].Nabla_w[n][k];205 } 206 } 207 } 208 } 209 void nnet_update_Nabla (N Net *nnet) {210 for (int n = 1; n < nnet[0].Nlayer; n++) {211 212 for (int j = 0; j < nnet[0].Nnodes[ n]; j++) {213 nnet[0]. Nabla_b[ n][j] += nnet[0].dNabla_b[n][j];214 215 for (int i = 0; i < nnet[0].Nnodes[ n-1]; i++) {216 int k = i + j*nnet[0].Nnodes[ n-1];217 nnet[0]. Nabla_w[ n][k] += nnet[0].dNabla_w[n][k];218 } 219 } 220 } 221 } 222 void nnet_reset_Nabla (N Net *nnet) {223 for (int n = 1; n < nnet[0].Nlayer; n++) {224 225 for (int j = 0; j < nnet[0].Nnodes[ n]; j++) {226 227 nnet[0]. Nabla_b[ n][j] = 0;228 nnet[0].dNabla_b[ n][j] = 0;229 230 for (int i = 0; i < nnet[0].Nnodes[ n-1]; i++) {231 int k = i + j*nnet[0].Nnodes[ n-1];232 nnet[0]. Nabla_w[ n][k] = 0;233 nnet[0].dNabla_w[ n][k] = 0;215 void nnet_apply_Nabla (Nnet *nnet, int Nmini, float eta) { 216 for (int L = 1; L < nnet[0].Nlayer; L++) { 217 218 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 219 nnet[0].biases[L][j] -= (eta / Nmini) * nnet[0].Nabla_b[L][j]; 220 221 for (int i = 0; i < nnet[0].Nnodes[L-1]; i++) { 222 int k = i + j*nnet[0].Nnodes[L-1]; 223 nnet[0].weight[L][k] -= (eta / Nmini) * nnet[0].Nabla_w[L][k]; 224 } 225 } 226 } 227 } 228 void nnet_update_Nabla (Nnet *nnet) { 229 for (int L = 1; L < nnet[0].Nlayer; L++) { 230 231 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 232 nnet[0]. Nabla_b[L][j] += nnet[0].dNabla_b[L][j]; 233 234 for (int i = 0; i < nnet[0].Nnodes[L-1]; i++) { 235 int k = i + j*nnet[0].Nnodes[L-1]; 236 nnet[0]. Nabla_w[L][k] += nnet[0].dNabla_w[L][k]; 237 } 238 } 239 } 240 } 241 void nnet_reset_Nabla (Nnet *nnet) { 242 for (int L = 1; L < nnet[0].Nlayer; L++) { 243 244 for (int j = 0; j < nnet[0].Nnodes[L]; j++) { 245 246 nnet[0]. Nabla_b[L][j] = 0; 247 nnet[0].dNabla_b[L][j] = 0; 248 249 for (int i = 0; i < nnet[0].Nnodes[L-1]; i++) { 250 int k = i + j*nnet[0].Nnodes[L-1]; 251 nnet[0]. Nabla_w[L][k] = 0; 252 nnet[0].dNabla_w[L][k] = 0; 234 253 } 235 254 } … … 238 257 239 258 // the input values must already be copied to the input layer svalue[] 240 void nnet_feedforward (N Net *nnet) {259 void nnet_feedforward (Nnet *nnet) { 241 260 242 261 for (int i = 1; i < nnet[0].Nlayer; i++) { … … 246 265 } 247 266 248 // calcula rte z, sigmoid(z) for each layer (z = w*value + bias)249 int nnet_onelayer (N Net *nnet, int n) {250 251 if ( n< 1) return FALSE; // abort here?252 if ( n>= nnet[0].Nlayer) return FALSE; // abort here?253 254 // evaluating a single layer [ n], n > 0, n < Nlayer:255 int Ninput = nnet[0].Nnodes[ n- 1];256 int Noutput = nnet[0].Nnodes[ n];257 258 // input layer is [ n-1], output layer is [n]267 // calculate z, sigmoid(z) for each layer (z = w*value + bias) 268 int nnet_onelayer (Nnet *nnet, int L) { 269 270 if (L < 1) return FALSE; // abort here? 271 if (L >= nnet[0].Nlayer) return FALSE; // abort here? 272 273 // evaluating a single layer [L], n > 0, n < Nlayer: 274 int Ninput = nnet[0].Nnodes[L - 1]; 275 int Noutput = nnet[0].Nnodes[L]; 276 277 // input layer is [L-1], output layer is [L] 259 278 for (int j = 0; j < Noutput; j ++) { 260 279 float sum = 0; … … 262 281 // weight matrix order is (0, 1, ... Ninput-1, Ninput, Ninput + 1, ... Ninput * Noutput - 1) 263 282 int k = j * Ninput + i; 264 sum += nnet[0].weight[ n][k]*nnet[0].svalue[n-1][i];265 } 266 sum += nnet[0].biases[ n][j];267 nnet[0].zvalue[ n][j] = sum;268 nnet[0].svalue[ n][j] = nnet_sigmoid(sum);269 nnet[0].sprime[ n][j] = nnet[0].svalue[n][j] * (1 - nnet[0].svalue[n][j]);283 sum += nnet[0].weight[L][k]*nnet[0].svalue[L-1][i]; 284 } 285 sum += nnet[0].biases[L][j]; 286 nnet[0].zvalue[L][j] = sum; 287 nnet[0].svalue[L][j] = nnet_sigmoid(sum); 288 nnet[0].sprime[L][j] = nnet[0].svalue[L][j] * (1 - nnet[0].svalue[L][j]); 270 289 // note that d sigmoid / dz = sigmoid * (1 - sigmoid) 271 290 } … … 274 293 275 294 float nnet_sigmoid (float value) { 276 return 1.0 / (1.0 + exp(- sum));277 } 278 279 void sortseq (float *X, int *Y, int N) {295 return 1.0 / (1.0 + exp(-value)); 296 } 297 298 void nnet_sortseq (float *X, int *Y, int N) { 280 299 281 300 # define SWAPFUNC(A,B){ float ftmp; int itmp; \
Note:
See TracChangeset
for help on using the changeset viewer.
