質問編集履歴

1

最初の質問時には、tensorflow.jsを使用していました。今ではtensorflow.jsを使用していません。

2020/12/02 15:07

投稿

TakeshiSue
TakeshiSue

スコア1

test CHANGED
@@ -1 +1 @@
1
- tensorflow.js分類問題を学習中minimizeで重みがNaNになるのを回避したい
1
+ console.log()表示される値がそれより下の行の関数の有無によってNaNになるか否かが変わるがなぜなのか、教えていだきたです。
test CHANGED
@@ -2,17 +2,37 @@
2
2
 
3
3
 
4
4
 
5
- JavaScriptとtensorflow.jsを使い、word2vecを作ろうとしています。
5
+ JavaScriptを使い、word2vecを作ろうとしています。
6
+
6
-
7
+ console.log()で表示される内容が、それよりも下の行に、とある自作の関数があるかないかで変わってしまいます。
8
+
7
-
9
+ その関数がある場合には要素がすべてNaNになった配列が、その関数がない場合には意図したとおりの数字を要素とした配列が、console.log()で表示されます。
10
+
11
+
12
+
8
-
13
+ > console.log(w1);
14
+
15
+ console.log(w2);
16
+
17
+ document.write(w1)
18
+
19
+ document.write('<br>↑w1<br>')
20
+
21
+ document.write(w2)
22
+
23
+ document.write('<br>↑w2<br>')
24
+
25
+ trainWord() //26行をコメントアウトすると、w1,w2がconsole.log()上でNaNになることがない。trainWordの奥深く、94行だけをコメントアウトしても、w1,w2がconsole.log()上でNaNになることがない。
26
+
27
+
28
+
29
+ 下記のソースコード26行目(trainWord())をコメントアウトするとw1,w2がconsole.log()でもdocument.write()でも意図したとおりの数字を表示します。
30
+
9
- optimizer.minimize(() => loss(predict(anIndex), oneHotVector[vectoredText[i]]));
31
+ 26行目があると、w1,w2がconsole.log()上で要素がNaNになります。しかし、document.write()では意図した数字を表示します。
10
-
11
-
12
-
13
- ここを越えると、重み行列w1,w2の中身がすべてNaNになってしまいます。
32
+
14
-
33
+
34
+
15
- NaNなっしまう原因と、NaNにることを防ぐ方法を、教えていただけまでしょうか
35
+ trainWord()関数がそれより上の行のconsole.log()の結果干渉しいるのはぜなのか、教えていただきたいです。
16
36
 
17
37
 
18
38
 
@@ -38,143 +58,269 @@
38
58
 
39
59
  <button type="button" class="controler" id="start" onclick="startClicked()">Start</button>
40
60
 
41
- <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
42
-
43
61
 
44
62
 
45
63
  <script>
46
64
 
65
+
66
+
67
+ //startボタンを押すと、テキストエリアの内容について、辞書化から教育までが、行われる。
68
+
69
+ function startClicked(){
70
+
71
+ text = document.getElementById('enterYourCode').value;
72
+
73
+ makeDictionary(text);
74
+
75
+ makeVectoredText();
76
+
77
+ oneHotVector = makeOneHotVector(dictionary.length);
78
+
79
+ w1 = createW(dictionary.length, hiddenSize);
80
+
81
+ w2 = createW(hiddenSize, dictionary.length);
82
+
83
+ console.log(w1);
84
+
85
+ console.log(w2);
86
+
87
+ document.write(w1)
88
+
89
+ document.write('<br>↑w1<br>')
90
+
91
+ document.write(w2)
92
+
93
+ document.write('<br>↑w2<br>')
94
+
95
+ trainWord() //26行をコメントアウトすると、w1,w2がconsole.log()上でNaNになることがない。trainWordの奥深く、94行だけをコメントアウトしても、w1,w2がconsole.log()上でNaNになることがない。
96
+
97
+ };
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+ //基本的な関数の宣言
106
+
107
+
108
+
109
+ //合計
110
+
111
+ var sum = function(arr) {
112
+
113
+ return arr.reduce(function(prev, current, i, arr) {
114
+
115
+ return prev+current;
116
+
117
+ });
118
+
119
+ };
120
+
121
+
122
+
123
+ //行列の積
124
+
125
+ function dot(a, b) {
126
+
127
+ var aNumRows = a.length
128
+
129
+ var aNumCols = a[0].length
130
+
131
+ var bNumRows = b.length
132
+
133
+ var bNumCols = b[0].length
134
+
135
+ m = new Array(aNumRows); // initialize array of rows
136
+
137
+ for (var r = 0; r < aNumRows; ++r) {
138
+
139
+ m[r] = new Array(bNumCols); // initialize the current row
140
+
141
+ for (var c = 0; c < bNumCols; ++c) {
142
+
143
+ m[r][c] = 0; // initialize the current cell
144
+
145
+ for (var i = 0; i < aNumCols; ++i) {
146
+
147
+ m[r][c] += a[r][i] * b[i][c];
148
+
149
+ }
150
+
151
+ }
152
+
153
+ }
154
+
155
+ return m;
156
+
157
+ }
158
+
159
+
160
+
161
+ //ソフトマックス関数
162
+
163
+ function softMax(array){
164
+
165
+ return array.map(element => Math.exp(element)/sum(array.map(element => Math.exp(element))))
166
+
167
+ }
168
+
169
+
170
+
171
+ //w1で使用するランダムな重みを作るための関数
172
+
173
+ function rnorm(){
174
+
175
+ return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());
176
+
177
+ }
178
+
179
+
180
+
47
181
  //各種変数の宣言
48
182
 
49
- var hiddenSize = 300;
50
-
51
- var text = undefined
52
-
53
- var dictionary = undefined
54
-
55
- var textSplit = undefined
56
-
57
- var oneHotVector = null
58
-
59
- var w1 = undefined
60
-
61
- var w2 = undefined
62
-
63
- var vectoredText = undefined
64
-
65
- const predict = anIndex => tf.tensor(oneHotVector[vectoredText[anIndex]]).dot(w1).dot(w2).softmax();
66
-
67
- const loss = function(onehotLabel,logits){return tf.losses.softmaxCrossEntropy(onehotLabel,logits)};
68
-
69
- const learningRate = 0.01;
70
-
71
- const optimizer = tf.train.adam(learningRate);
72
-
73
-
74
-
75
- //w1で使用するランダムな重みを作るための関数
76
-
77
- function rnorm(){
78
-
79
- return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());
183
+ var hiddenSize = 3; //隠れ層の数。本来は300
184
+
185
+ var text = undefined //もととなる文章
186
+
187
+ var dictionary = undefined //文章に出てきた単語から、重複するものを除いたもの
188
+
189
+ var textSplit = undefined //もととなる文章を、単語ごとの区切りで配列にしたもの
190
+
191
+ var oneHotVector = null //辞書に表れる単語を、ワンホットベクトルで表したもの
192
+
193
+ var w1 = undefined //1層目の重み
194
+
195
+ var w2 = undefined //2層目の重み
196
+
197
+ var vectoredText = undefined //単語で区切った文章を、ワンホットベクトル上のインデックスで表したもの
198
+
199
+ const learningRate = 0.01; //学習率
200
+
201
+ const epsilon = 0.00100000001; //微分に使用する極めて小さい数
202
+
203
+
204
+
205
+
206
+
207
+ function predict(foo,weight1,weight2){//foo には anIndex が入る予定
208
+
209
+ let b = []
210
+
211
+ b[0]= oneHotVector[vectoredText[foo]]
212
+
213
+ let inputToHidden = dot(b,weight1);
214
+
215
+ let HiddenToOutput = dot(inputToHidden,weight2)
216
+
217
+ return softMax(HiddenToOutput[0])
218
+
219
+ }
220
+
221
+
222
+
223
+
224
+
225
+ function loss(foo,logits){//foo には anIndex が入る予定, logits には predict()が入る予定
226
+
227
+ let resultVector = oneHotVector[vectoredText[foo]].map(x => x)
228
+
229
+ for(let i = 0; i<resultVector.length; i = i+1){
230
+
231
+ resultVector[i] = resultVector[i]*Math.log(logits[i]) //94行をコメントアウトすると、console.log()でw1,w2がNaNになることがない。
232
+
233
+ }
234
+
235
+ return -1*sum(resultVector)};
236
+
237
+
238
+
239
+ //入力されたテキストから、1回の単語が1回のみ表れる辞書を作る。ただし、所有格の’sは' `s'として、1つの単語として扱う。
240
+
241
+ function makeDictionary(){
242
+
243
+ let aposS = /'s/g;
244
+
245
+ let regex = /.|'|"|\,/g;
246
+
247
+ textSplit = text.toLowerCase().replace(aposS,' `s').replace(regex,' ').split(' ');
248
+
249
+ dictionary =[]
250
+
251
+ for(let i = 0; i < textSplit.length ; i = i+1){
252
+
253
+ if(dictionary.includes(textSplit[i])){
254
+
255
+ let j = textSplit.length;
256
+
257
+ textSplit = textSplit.filter(arg => arg !=textSplit[i]);
258
+
259
+ i = i - (j-textSplit.length);
260
+
261
+ } else {
262
+
263
+ dictionary.push(textSplit[i]);
264
+
265
+ };
266
+
267
+ };
268
+
269
+ return null;
80
270
 
81
271
  }
82
272
 
83
273
 
84
274
 
85
- //入力されたテキスから1回の単語が1み表れる辞書を作る。ただし、所有格の’sは' `s'として、1つの単語として扱う。
275
+ //辞書に対応するワンホッベクトルを対角成分が1の対角行列として作る
86
-
276
+
87
- function makeDictionary(){
277
+ function makeOneHotVector(number){
88
-
278
+
89
- let aposS = /'s/g;
279
+ let matrix= [...Array(number)]
90
-
91
- let regex = /.|'|"|\,/g;
280
+
92
-
93
- textSplit = text.toLowerCase().replace(aposS,' `s').replace(regex,' ').split(' ');
94
-
95
- dictionary =[]
96
-
97
- for(let i = 0; i < textSplit.length ; i = i+1){
281
+ for(let i = 0; i < number; i = i +1){
98
-
99
- if(dictionary.includes(textSplit[i])){
282
+
100
-
101
- let j = textSplit.length
102
-
103
- textSplit = textSplit.filter(arg => arg !=textSplit[i])
283
+ matrix[i] = [...Array(number)].map(arg => 0);
104
-
105
- i = i - (j-textSplit.length)
284
+
106
-
107
- } else {
108
-
109
- dictionary.push(textSplit[i]);
285
+ matrix[i][i] = 1;
110
-
111
- };
112
286
 
113
287
  };
114
288
 
289
+ return matrix;
290
+
291
+ };
292
+
293
+
294
+
295
+ //テキストに現れる単語を、その単語の辞書内でのインデックスに突き合わせることで、テキストをもとにしたベクトルを作る。
296
+
297
+ function makeVectoredText(){
298
+
299
+ vectoredText = []
300
+
301
+ textSplit.forEach(element => vectoredText.push(dictionary.indexOf(element)));
302
+
115
303
  return null;
116
304
 
117
305
  }
118
306
 
119
307
 
120
308
 
121
- //辞書に対応するワットベクを、対角成分が1の対角行列として作る
309
+ //ットサイズ(is)とアウプットサイズ(os)もとに重みを表現する行列作る
122
-
310
+
123
- function makeOneHotVector(number){
311
+ function createW(inputSize, outputSize){
124
-
125
- let matrix= [...Array(number)]
312
+
126
-
127
- for(let i = 0; i < number; i = i +1){
128
-
129
- matrix[i] = [...Array(number)].map(arg => 0);
313
+ return [...Array(inputSize)].map(() => [...Array(outputSize)].map(() => rnorm()/Math.sqrt(inputSize)));
130
-
131
- matrix[i][i] = 1;
132
314
 
133
315
  };
134
316
 
135
- return matrix;
136
-
137
- };
138
-
139
-
140
-
141
- //テキストに現れる単語を、その単語の辞書内でのインデックスに突き合わせることで、テキストをもとにしたベクトルを作る。
142
-
143
- function makeVectoredText(){
144
-
145
- vectoredText = []
146
-
147
- textSplit.forEach(element => vectoredText.push(dictionary.indexOf(element)));
148
-
149
- return null;
150
-
151
- }
152
-
153
-
154
-
155
- //インプットサイズ(is)とアウトプットサイズ(os)をもとに、重みを表現する行列を作る。
156
-
157
- function createW(is, os){
158
-
159
- let matrix= [...Array(is)]
160
-
161
- for(let i = 0; i < is; i = i +1){
162
-
163
- matrix[i] = [...Array(os)].map(() => rnorm()/Math.sqrt(is));
164
-
165
- };
166
-
167
- return tf.variable(tf.tensor(matrix));
168
-
169
- };
170
-
171
317
 
172
318
 
173
319
  //textの(実際にはvectoredTextの)最初の単語から、windowサイズ5で、教育を行う。
174
320
 
175
321
  function trainWord(){
176
322
 
177
- for(i=0; i <= vectoredText.length-1 ; i= i+1){
323
+ for(let i=0; i <= vectoredText.length-1 ; i= i+1){
178
324
 
179
325
  // target = makeOneHotVector[vectoredText[i]]
180
326
 
@@ -184,27 +330,65 @@
184
330
 
185
331
  for(anIndex of indicesAroundTarget){
186
332
 
333
+
334
+
335
+ let copyOfW1 = w1.map(x => x);
336
+
337
+ let copyOfW2 = w2.map(x => x);
338
+
339
+ let newW1 = w1.map(x => x);
340
+
341
+ let newW2 = w2.map(x => x);
342
+
343
+
344
+
345
+
346
+
347
+ afterThen();
348
+
349
+ w1 = newW1.map(x => x);
350
+
351
+ w2 = newW2.map(x => x);
352
+
353
+
354
+
355
+ function afterThen(){
356
+
187
- /*
357
+ //w1の学習
188
-
189
- document.write(i+ 'th iteration. anIndex is ' + anIndex +'<br>' )
358
+
190
-
191
- document.write(tf.tensor(oneHotVector[vectoredText[anIndex]]).dot(w1).dot(w2).softmax()+'<br>')
192
-
193
- document.write(predict(anIndex)+' is (predict(anIndex))<br>')
359
+ for(let j = 0; j < newW1.length; j = j+1){
194
-
195
- document.write(oneHotVector[vectoredText[i]]+' is oneHotVector[vectoredText[i]]<br>')
360
+
196
-
197
- document.write(loss(predict(anIndex), oneHotVector[vectoredText[i]])+' is loss(predict(anIndex), oneHotVector[vectoredText[i]])<br>')
198
-
199
- document.write(w1+ ' is w1<br>')
361
+ for(let k = 0; k < newW1[j].length; k = k+1){
362
+
200
-
363
+ let interimW1 = w1.map(x => x);
364
+
365
+ interimW1[j][k] = interimW1[j][k]+epsilon;
366
+
367
+ newW1[j][k] = interimW1[j][k]-learningRate*( loss(anIndex,predict(anIndex,interimW1,w2) - loss(anIndex,predict(anIndex,w1,w2))))/epsilon;
368
+
369
+ }
370
+
371
+ }
372
+
373
+ //w2の学習
374
+
375
+ for(let j = 0; j < newW2.length; j = j+1){
376
+
201
- document.write(w2+ ' is w2<br>')
377
+ for(let k = 0; k < newW2[j].length; k = k+1){
202
-
378
+
203
- */
379
+ let interimW2 = w2.map(x => x);
204
-
380
+
205
- //minimizeを行うと、w1とw2の要素の値がNaNになる。
381
+ interimW2[j][k] = interimW2[j][k]+epsilon;
206
-
382
+
207
- optimizer.minimize(() => loss(predict(anIndex), oneHotVector[vectoredText[i]]));
383
+ newW2[j][k] = interimW2[j][k]-learningRate*( loss(anIndex,predict(anIndex,w1,interimW2) - loss(anIndex,predict(anIndex,w1,w2))))/epsilon;
384
+
385
+ }
386
+
387
+ }
388
+
389
+ return null
390
+
391
+ }
208
392
 
209
393
  }
210
394
 
@@ -214,34 +398,6 @@
214
398
 
215
399
 
216
400
 
217
-
218
-
219
- //startボタンを押すと、テキストエリアの内容について、辞書化から教育までが、行われる。
220
-
221
- function startClicked(){
222
-
223
- text = document.getElementById('enterYourCode').value
224
-
225
- makeDictionary(text);
226
-
227
- makeVectoredText()
228
-
229
- oneHotVector = makeOneHotVector(dictionary.length)
230
-
231
- //hidden layer size = 300
232
-
233
- w1 = createW(dictionary.length, hiddenSize)
234
-
235
- w2 = createW(hiddenSize, dictionary.length)
236
-
237
-
238
-
239
- trainWord()
240
-
241
- };
242
-
243
-
244
-
245
401
  </script>
246
402
 
247
403
 
@@ -250,6 +406,4 @@
250
406
 
251
407
  </html>
252
408
 
253
-
254
-
255
409
  ```