learning_model 1.0.14 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.LearningMobilenetImage = exports.LearningImage = void 0;
7
- const image_1 = __importDefault(require("./learning/image"));
7
+ var image_1 = __importDefault(require("./learning/image"));
8
8
  exports.LearningImage = image_1.default;
9
- const mobilenet_image_1 = __importDefault(require("./learning/mobilenet_image"));
9
+ var mobilenet_image_1 = __importDefault(require("./learning/mobilenet_image"));
10
10
  exports.LearningMobilenetImage = mobilenet_image_1.default;
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __generator = (this && this.__generator) || function (thisArg, body) {
35
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
36
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
37
+ function verb(n) { return function (v) { return step([n, v]); }; }
38
+ function step(op) {
39
+ if (f) throw new TypeError("Generator is already executing.");
40
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
41
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
42
+ if (y = 0, t) op = [op[0] & 2, t.value];
43
+ switch (op[0]) {
44
+ case 0: case 1: t = op; break;
45
+ case 4: _.label++; return { value: op[1], done: false };
46
+ case 5: _.label++; y = op[1]; op = [0]; continue;
47
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
48
+ default:
49
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
50
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
51
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
52
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
53
+ if (t[2]) _.ops.pop();
54
+ _.trys.pop(); continue;
55
+ }
56
+ op = body.call(thisArg, _);
57
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
58
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
59
+ }
60
+ };
61
+ Object.defineProperty(exports, "__esModule", { value: true });
62
+ var tf = __importStar(require("@tensorflow/tfjs"));
63
+ var LearningImage = /** @class */ (function () {
64
+ function LearningImage(_a) {
65
+ var _b = _a === void 0 ? {} : _a, _c = _b.epochs, epochs = _c === void 0 ? 10 : _c, _d = _b.batchSize, batchSize = _d === void 0 ? 16 : _d, _e = _b.limitSize, limitSize = _e === void 0 ? 2 : _e, _f = _b.learningRate, learningRate = _f === void 0 ? 0.001 : _f;
66
+ this.trainImages = [];
67
+ this.MOBILE_NET_INPUT_WIDTH = 224;
68
+ this.MOBILE_NET_INPUT_HEIGHT = 224;
69
+ this.MOBILE_NET_INPUT_CHANNEL = 3;
70
+ this.IMAGE_NORMALIZATION_FACTOR = 255.0;
71
+ this.onProgress = function () { };
72
+ this.onLoss = function () { };
73
+ this.onEvents = function () { };
74
+ this.onTrainBegin = function () { };
75
+ this.onTrainEnd = function () { };
76
+ this.model = null;
77
+ this.epochs = epochs;
78
+ this.batchSize = batchSize;
79
+ this.learningRate = learningRate;
80
+ this.labels = [];
81
+ this.isRunning = false;
82
+ this.isReady = false;
83
+ this.limitSize = 2;
84
+ }
85
+ // 학습 데이타 등록
86
+ LearningImage.prototype.addData = function (label, data) {
87
+ return __awaiter(this, void 0, void 0, function () {
88
+ var tensor;
89
+ return __generator(this, function (_a) {
90
+ try {
91
+ tensor = tf.browser.fromPixels(data);
92
+ console.log('addData', tensor);
93
+ this.trainImages.push(tensor);
94
+ this.labels.push(label);
95
+ if (this.labels.length >= this.limitSize) {
96
+ this.isReady = true;
97
+ }
98
+ return [2 /*return*/, Promise.resolve()];
99
+ }
100
+ catch (error) {
101
+ console.error('Model training failed', error);
102
+ throw error;
103
+ }
104
+ return [2 /*return*/];
105
+ });
106
+ });
107
+ };
108
+ // 모델 학습 처리
109
+ LearningImage.prototype.train = function () {
110
+ return __awaiter(this, void 0, void 0, function () {
111
+ var customCallback, _a, inputData, targetData, history_1, error_1;
112
+ var _this = this;
113
+ return __generator(this, function (_b) {
114
+ switch (_b.label) {
115
+ case 0:
116
+ if (this.isRunning) {
117
+ return [2 /*return*/, Promise.reject(new Error('Training is already in progress.'))];
118
+ }
119
+ customCallback = {
120
+ onTrainBegin: function (log) {
121
+ _this.onTrainBegin(log);
122
+ console.log('Training has started.');
123
+ },
124
+ onTrainEnd: function (log) {
125
+ _this.onTrainEnd(log);
126
+ console.log('Training has ended.');
127
+ _this.isRunning = false;
128
+ },
129
+ onBatchBegin: function (batch, logs) {
130
+ console.log("Batch ".concat(batch, " is starting."));
131
+ },
132
+ onBatchEnd: function (batch, logs) {
133
+ console.log("Batch ".concat(batch, " has ended."));
134
+ },
135
+ onEpochBegin: function (epoch, logs) {
136
+ console.log("Epoch ".concat(epoch + 1, " is starting."), logs);
137
+ },
138
+ onEpochEnd: function (epoch, logs) {
139
+ console.log("Epoch ".concat(epoch + 1, " has ended."));
140
+ _this.onLoss(logs.loss);
141
+ console.log('Loss:', logs);
142
+ _this.onEvents(logs);
143
+ _this.onProgress(epoch + 1);
144
+ }
145
+ };
146
+ _b.label = 1;
147
+ case 1:
148
+ _b.trys.push([1, 4, , 5]);
149
+ this.isRunning = true;
150
+ if (this.labels.length < this.limitSize) {
151
+ return [2 /*return*/, Promise.reject(new Error('Please train Data need over 2 data length'))];
152
+ }
153
+ _a = this;
154
+ return [4 /*yield*/, this._createModel(this.labels.length)];
155
+ case 2:
156
+ _a.model = _b.sent();
157
+ inputData = this._preprocessedInputData(this.model);
158
+ console.log('inputData', inputData);
159
+ targetData = this._preprocessedTargetData();
160
+ console.log('targetData', targetData);
161
+ return [4 /*yield*/, this.model.fit(inputData, targetData, {
162
+ epochs: this.epochs,
163
+ batchSize: this.batchSize,
164
+ callbacks: customCallback
165
+ })];
166
+ case 3:
167
+ history_1 = _b.sent();
168
+ console.log('Model training completed', history_1);
169
+ return [2 /*return*/, history_1];
170
+ case 4:
171
+ error_1 = _b.sent();
172
+ this.isRunning = false;
173
+ console.error('Model training failed', error_1);
174
+ throw error_1;
175
+ case 5: return [2 /*return*/];
176
+ }
177
+ });
178
+ });
179
+ };
180
+ // 추론하기
181
+ LearningImage.prototype.infer = function (data) {
182
+ return __awaiter(this, void 0, void 0, function () {
183
+ var tensor, resizedTensor, reshapedTensor, predictions, predictionsData, classProbabilities, i, className, probability, existingProbability, error_2;
184
+ return __generator(this, function (_a) {
185
+ switch (_a.label) {
186
+ case 0:
187
+ if (this.model === null) {
188
+ throw new Error('Model is null');
189
+ }
190
+ _a.label = 1;
191
+ case 1:
192
+ _a.trys.push([1, 3, , 4]);
193
+ tensor = tf.browser.fromPixels(data);
194
+ resizedTensor = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
195
+ reshapedTensor = resizedTensor.expandDims(0);
196
+ predictions = this.model.predict(reshapedTensor);
197
+ return [4 /*yield*/, predictions.data()];
198
+ case 2:
199
+ predictionsData = _a.sent();
200
+ classProbabilities = new Map();
201
+ for (i = 0; i < predictionsData.length; i++) {
202
+ className = this.labels[i];
203
+ probability = predictionsData[i];
204
+ existingProbability = classProbabilities.get(className);
205
+ if (existingProbability !== undefined) {
206
+ classProbabilities.set(className, existingProbability + probability);
207
+ }
208
+ else {
209
+ classProbabilities.set(className, probability);
210
+ }
211
+ }
212
+ console.log('Class Probabilities:', classProbabilities);
213
+ return [2 /*return*/, classProbabilities];
214
+ case 3:
215
+ error_2 = _a.sent();
216
+ throw error_2;
217
+ case 4: return [2 /*return*/];
218
+ }
219
+ });
220
+ });
221
+ };
222
+ // 모델 저장
223
+ LearningImage.prototype.saveModel = function () {
224
+ console.log('saved model');
225
+ };
226
+ // 진행중 여부
227
+ LearningImage.prototype.running = function () {
228
+ return this.isRunning;
229
+ };
230
+ LearningImage.prototype.ready = function () {
231
+ return this.isReady;
232
+ };
233
+ // target 라벨 데이타
234
+ LearningImage.prototype._preprocessedTargetData = function () {
235
+ var _this = this;
236
+ // 라벨 unique 처리 & 배열 리턴
237
+ console.log('uniqueLabels.length', this.labels, this.labels.length);
238
+ var labelIndices = this.labels.map(function (label) { return _this.labels.indexOf(label); });
239
+ console.log('labelIndices', labelIndices);
240
+ var oneHotEncode = tf.oneHot(tf.tensor1d(labelIndices, 'int32'), this.labels.length);
241
+ console.log('oneHotEncode', oneHotEncode);
242
+ return oneHotEncode;
243
+ };
244
+ // 입력 이미지 데이타
245
+ LearningImage.prototype._preprocessedInputData = function (model) {
246
+ var _this = this;
247
+ // 이미지 배열을 배치로 변환 - [null, 224, 224, 3]
248
+ var inputShape = model.inputs[0].shape;
249
+ console.log('inputShape', inputShape);
250
+ // inputShape를 이와 같이 포멧 맞춘다. for reshape to [224, 224, 3]
251
+ var inputShapeArray = inputShape.slice(1);
252
+ console.log('inputShapeArray', inputShapeArray);
253
+ var inputBatch = tf.stack(this.trainImages.map(function (image) {
254
+ // 이미지 전처리 및 크기 조정 등을 수행한 후에
255
+ // 모델의 입력 형태로 변환하여 반환
256
+ var xs = _this._preprocessData(image); // 전처리 함수는 사용자 정의해야 함
257
+ return tf.reshape(xs, inputShapeArray);
258
+ }));
259
+ return inputBatch;
260
+ };
261
+ // 모델 학습하기 위한 데이타 전처리 단계
262
+ LearningImage.prototype._preprocessData = function (tensor) {
263
+ try {
264
+ // mobilenet model summary를 하면 위와 같이 224,224 사이즈의 입력값 설정되어 있다. ex) input_1 (InputLayer) [null,224,224,3]
265
+ var resizedImage = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
266
+ // 이미지를 [0,1] 범위로 정규화 255로 나뉜 픽셀값
267
+ var normalizedImage = resizedImage.div(this.IMAGE_NORMALIZATION_FACTOR);
268
+ // expandDims(0)을 하여 차원을 추가하여 4D텐서 반환
269
+ return normalizedImage.expandDims(0);
270
+ }
271
+ catch (error) {
272
+ console.error('Failed to _preprocessData data', error);
273
+ throw error;
274
+ }
275
+ };
276
+ // 모델 저장
277
+ LearningImage.prototype._createModel = function (numClasses) {
278
+ return __awaiter(this, void 0, void 0, function () {
279
+ var inputShape, model, optimizer;
280
+ return __generator(this, function (_a) {
281
+ try {
282
+ inputShape = [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT, this.MOBILE_NET_INPUT_CHANNEL];
283
+ model = tf.sequential();
284
+ model.add(tf.layers.conv2d({
285
+ inputShape: inputShape,
286
+ filters: 32,
287
+ kernelSize: 3,
288
+ activation: 'relu'
289
+ }));
290
+ model.add(tf.layers.maxPooling2d({ poolSize: 2 }));
291
+ model.add(tf.layers.conv2d({
292
+ filters: 64,
293
+ kernelSize: 3,
294
+ activation: 'relu'
295
+ }));
296
+ model.add(tf.layers.maxPooling2d({ poolSize: 2 }));
297
+ model.add(tf.layers.flatten());
298
+ model.add(tf.layers.dense({
299
+ units: numClasses,
300
+ activation: 'softmax'
301
+ }));
302
+ optimizer = tf.train.adam(this.learningRate);
303
+ model.compile({
304
+ loss: (numClasses === 2) ? 'binaryCrossentropy' : 'categoricalCrossentropy',
305
+ optimizer: optimizer,
306
+ metrics: ['accuracy', 'acc']
307
+ });
308
+ model.summary();
309
+ return [2 /*return*/, model];
310
+ }
311
+ catch (error) {
312
+ console.error('Failed to load model', error);
313
+ throw error;
314
+ }
315
+ return [2 /*return*/];
316
+ });
317
+ });
318
+ };
319
+ return LearningImage;
320
+ }());
321
+ exports.default = LearningImage;
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ ///////////////////////////////////////////////////////////////////////////
3
+ ///////////////////////////////////////////////////////////////////////////
4
+ ///////////////////////////////////////////////////////////////////////////
5
+ // mobilenet 모델을 이용한 전이학습 방법
6
+ ///////////////////////////////////////////////////////////////////////////
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || function (mod) {
24
+ if (mod && mod.__esModule) return mod;
25
+ var result = {};
26
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
+ __setModuleDefault(result, mod);
28
+ return result;
29
+ };
30
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
31
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
32
+ return new (P || (P = Promise))(function (resolve, reject) {
33
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
34
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
35
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
36
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
37
+ });
38
+ };
39
+ var __generator = (this && this.__generator) || function (thisArg, body) {
40
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
41
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
42
+ function verb(n) { return function (v) { return step([n, v]); }; }
43
+ function step(op) {
44
+ if (f) throw new TypeError("Generator is already executing.");
45
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
46
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
47
+ if (y = 0, t) op = [op[0] & 2, t.value];
48
+ switch (op[0]) {
49
+ case 0: case 1: t = op; break;
50
+ case 4: _.label++; return { value: op[1], done: false };
51
+ case 5: _.label++; y = op[1]; op = [0]; continue;
52
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
53
+ default:
54
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
55
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
56
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
57
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
58
+ if (t[2]) _.ops.pop();
59
+ _.trys.pop(); continue;
60
+ }
61
+ op = body.call(thisArg, _);
62
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
63
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
64
+ }
65
+ };
66
+ Object.defineProperty(exports, "__esModule", { value: true });
67
+ var tf = __importStar(require("@tensorflow/tfjs"));
68
+ var LearningMobilenetImage = /** @class */ (function () {
69
+ function LearningMobilenetImage(_a) {
70
+ var _b = _a === void 0 ? {} : _a, _c = _b.modelURL, modelURL = _c === void 0 ? 'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json' : _c, // 디폴트 mobilenet 이미지
71
+ _d = _b.epochs, // 디폴트 mobilenet 이미지
72
+ epochs = _d === void 0 ? 10 : _d, _e = _b.batchSize, batchSize = _e === void 0 ? 16 : _e, _f = _b.limitSize, limitSize = _f === void 0 ? 2 : _f, _g = _b.learningRate, learningRate = _g === void 0 ? 0.001 : _g;
73
+ this.trainImages = [];
74
+ this.MOBILE_NET_INPUT_WIDTH = 224;
75
+ this.MOBILE_NET_INPUT_HEIGHT = 224;
76
+ this.MOBILE_NET_INPUT_CHANNEL = 3;
77
+ this.IMAGE_NORMALIZATION_FACTOR = 255.0;
78
+ // 진행 상태를 나타내는 이벤트를 정의합니다.
79
+ this.onProgress = function () { };
80
+ this.onLoss = function () { };
81
+ this.onEvents = function () { };
82
+ this.onTrainBegin = function () { };
83
+ this.onTrainEnd = function () { };
84
+ this.model = null;
85
+ this.epochs = epochs;
86
+ this.batchSize = batchSize;
87
+ this.learningRate = learningRate;
88
+ this.labels = [];
89
+ this.modelURL = modelURL;
90
+ this.isRunning = false;
91
+ this.isReady = false;
92
+ this.limitSize = limitSize;
93
+ }
94
+ // 학습 데이타 등록
95
+ LearningMobilenetImage.prototype.addData = function (label, data) {
96
+ return __awaiter(this, void 0, void 0, function () {
97
+ var tensor;
98
+ return __generator(this, function (_a) {
99
+ try {
100
+ tensor = tf.browser.fromPixels(data);
101
+ console.log('addData', tensor);
102
+ this.trainImages.push(tensor);
103
+ this.labels.push(label);
104
+ if (this.labels.length >= this.limitSize) {
105
+ this.isReady = true;
106
+ }
107
+ return [2 /*return*/, Promise.resolve()];
108
+ }
109
+ catch (error) {
110
+ console.error('Model training failed', error);
111
+ throw error;
112
+ }
113
+ return [2 /*return*/];
114
+ });
115
+ });
116
+ };
117
+ // 모델 학습 처리
118
+ LearningMobilenetImage.prototype.train = function () {
119
+ return __awaiter(this, void 0, void 0, function () {
120
+ var customCallback, _a, inputData, targetData, history_1, error_1;
121
+ var _this = this;
122
+ return __generator(this, function (_b) {
123
+ switch (_b.label) {
124
+ case 0:
125
+ if (this.isRunning) {
126
+ return [2 /*return*/, Promise.reject(new Error('Training is already in progress.'))];
127
+ }
128
+ customCallback = {
129
+ onTrainBegin: function (log) {
130
+ _this.onTrainBegin(log);
131
+ console.log('Training has started.');
132
+ },
133
+ onTrainEnd: function (log) {
134
+ _this.onTrainEnd(log);
135
+ console.log('Training has ended.');
136
+ _this.isRunning = false;
137
+ },
138
+ onBatchBegin: function (batch, logs) {
139
+ console.log("Batch ".concat(batch, " is starting."));
140
+ },
141
+ onBatchEnd: function (batch, logs) {
142
+ console.log("Batch ".concat(batch, " has ended."));
143
+ },
144
+ onEpochBegin: function (epoch, logs) {
145
+ console.log("Epoch ".concat(epoch + 1, " is starting."), logs);
146
+ },
147
+ onEpochEnd: function (epoch, logs) {
148
+ console.log("Epoch ".concat(epoch + 1, " has ended."));
149
+ console.log('Loss:', logs);
150
+ _this.onLoss(logs.loss);
151
+ _this.onProgress(epoch + 1);
152
+ _this.onEvents(logs);
153
+ }
154
+ };
155
+ _b.label = 1;
156
+ case 1:
157
+ _b.trys.push([1, 4, , 5]);
158
+ this.isRunning = true;
159
+ if (this.labels.length < this.limitSize) {
160
+ return [2 /*return*/, Promise.reject(new Error('Please train Data need over 2 data length'))];
161
+ }
162
+ _a = this;
163
+ return [4 /*yield*/, this._createModel(this.labels.length)];
164
+ case 2:
165
+ _a.model = _b.sent();
166
+ inputData = this._preprocessedInputData(this.model);
167
+ targetData = this._preprocessedTargetData();
168
+ return [4 /*yield*/, this.model.fit(inputData, targetData, {
169
+ epochs: this.epochs,
170
+ batchSize: this.batchSize,
171
+ callbacks: customCallback
172
+ })];
173
+ case 3:
174
+ history_1 = _b.sent();
175
+ console.log('Model training completed', history_1);
176
+ return [2 /*return*/, history_1];
177
+ case 4:
178
+ error_1 = _b.sent();
179
+ this.isRunning = false;
180
+ console.error('Model training failed', error_1);
181
+ throw error_1;
182
+ case 5: return [2 /*return*/];
183
+ }
184
+ });
185
+ });
186
+ };
187
+ // 추론하기
188
+ LearningMobilenetImage.prototype.infer = function (data) {
189
+ return __awaiter(this, void 0, void 0, function () {
190
+ var tensor, resizedTensor, reshapedTensor, predictions, predictionsData, classProbabilities, i, className, probability, existingProbability, error_2;
191
+ return __generator(this, function (_a) {
192
+ switch (_a.label) {
193
+ case 0:
194
+ if (this.model === null) {
195
+ return [2 /*return*/, Promise.reject(new Error('Model is Null'))];
196
+ }
197
+ _a.label = 1;
198
+ case 1:
199
+ _a.trys.push([1, 3, , 4]);
200
+ tensor = tf.browser.fromPixels(data);
201
+ resizedTensor = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
202
+ reshapedTensor = resizedTensor.expandDims(0);
203
+ predictions = this.model.predict(reshapedTensor);
204
+ return [4 /*yield*/, predictions.data()];
205
+ case 2:
206
+ predictionsData = _a.sent();
207
+ classProbabilities = new Map();
208
+ for (i = 0; i < predictionsData.length; i++) {
209
+ className = this.labels[i];
210
+ probability = predictionsData[i];
211
+ existingProbability = classProbabilities.get(className);
212
+ if (existingProbability !== undefined) {
213
+ classProbabilities.set(className, existingProbability + probability);
214
+ }
215
+ else {
216
+ classProbabilities.set(className, probability);
217
+ }
218
+ }
219
+ console.log('Class Probabilities:', classProbabilities);
220
+ return [2 /*return*/, classProbabilities];
221
+ case 3:
222
+ error_2 = _a.sent();
223
+ throw error_2;
224
+ case 4: return [2 /*return*/];
225
+ }
226
+ });
227
+ });
228
+ };
229
+ // 모델 저장
230
+ LearningMobilenetImage.prototype.saveModel = function () {
231
+ console.log('saved model');
232
+ };
233
+ // 진행중 여부
234
+ LearningMobilenetImage.prototype.running = function () {
235
+ return this.isRunning;
236
+ };
237
+ LearningMobilenetImage.prototype.ready = function () {
238
+ return this.isReady;
239
+ };
240
+ // target 라벨 데이타
241
+ LearningMobilenetImage.prototype._preprocessedTargetData = function () {
242
+ var _this = this;
243
+ // 라벨 unique 처리 & 배열 리턴
244
+ console.log('uniqueLabels.length', this.labels, this.labels.length);
245
+ var labelIndices = this.labels.map(function (label) { return _this.labels.indexOf(label); });
246
+ console.log('labelIndices', labelIndices);
247
+ var oneHotEncode = tf.oneHot(tf.tensor1d(labelIndices, 'int32'), this.labels.length);
248
+ console.log('oneHotEncode', oneHotEncode);
249
+ return oneHotEncode;
250
+ };
251
+ // 입력 이미지 데이타
252
+ LearningMobilenetImage.prototype._preprocessedInputData = function (model) {
253
+ var _this = this;
254
+ // 이미지 배열을 배치로 변환 - [null, 224, 224, 3]
255
+ var inputShape = model.inputs[0].shape;
256
+ console.log('inputShape', inputShape);
257
+ // inputShape를 이와 같이 포멧 맞춘다. for reshape to [224, 224, 3]
258
+ var inputShapeArray = inputShape.slice(1);
259
+ console.log('inputShapeArray', inputShapeArray);
260
+ var inputBatch = tf.stack(this.trainImages.map(function (image) {
261
+ // 이미지 전처리 및 크기 조정 등을 수행한 후에
262
+ // 모델의 입력 형태로 변환하여 반환
263
+ var xs = _this._preprocessData(image); // 전처리 함수는 사용자 정의해야 함
264
+ return tf.reshape(xs, inputShapeArray);
265
+ }));
266
+ return inputBatch;
267
+ };
268
+ // 모델 학습하기 위한 데이타 전처리 단계
269
+ LearningMobilenetImage.prototype._preprocessData = function (tensor) {
270
+ try {
271
+ // mobilenet model summary를 하면 위와 같이 224,224 사이즈의 입력값 설정되어 있다. ex) input_1 (InputLayer) [null,224,224,3]
272
+ var resizedImage = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
273
+ // 이미지를 [0,1] 범위로 정규화 255로 나뉜 픽셀값
274
+ var normalizedImage = resizedImage.div(this.IMAGE_NORMALIZATION_FACTOR);
275
+ // expandDims(0)을 하여 차원을 추가하여 4D텐서 반환
276
+ return normalizedImage.expandDims(0);
277
+ }
278
+ catch (error) {
279
+ console.error('Failed to _preprocessData data', error);
280
+ throw error;
281
+ }
282
+ };
283
+ // 모델 저장
284
+ LearningMobilenetImage.prototype._createModel = function (numClasses) {
285
+ return __awaiter(this, void 0, void 0, function () {
286
+ var load_model, truncatedModel, _i, _a, layer, model, optimizer, error_3;
287
+ return __generator(this, function (_b) {
288
+ switch (_b.label) {
289
+ case 0:
290
+ _b.trys.push([0, 2, , 3]);
291
+ return [4 /*yield*/, tf.loadLayersModel(this.modelURL)];
292
+ case 1:
293
+ load_model = _b.sent();
294
+ truncatedModel = tf.model({
295
+ inputs: load_model.inputs,
296
+ outputs: load_model.layers[load_model.layers.length - 2].output
297
+ });
298
+ // 모델을 학습 가능하게 설정하고 선택한 레이어까지 고정
299
+ for (_i = 0, _a = truncatedModel.layers; _i < _a.length; _i++) {
300
+ layer = _a[_i];
301
+ layer.trainable = false;
302
+ }
303
+ model = tf.sequential();
304
+ model.add(truncatedModel);
305
+ model.add(tf.layers.flatten()); // 필요한 경우 Flatten 레이어 추가
306
+ model.add(tf.layers.dense({
307
+ units: numClasses,
308
+ activation: 'softmax'
309
+ }));
310
+ optimizer = tf.train.adam(this.learningRate);
311
+ model.compile({
312
+ loss: (numClasses === 2) ? 'binaryCrossentropy' : 'categoricalCrossentropy',
313
+ optimizer: optimizer,
314
+ metrics: ['accuracy', 'acc']
315
+ });
316
+ model.summary();
317
+ return [2 /*return*/, model];
318
+ case 2:
319
+ error_3 = _b.sent();
320
+ console.error('Failed to load model', error_3);
321
+ throw error_3;
322
+ case 3: return [2 /*return*/];
323
+ }
324
+ });
325
+ });
326
+ };
327
+ return LearningMobilenetImage;
328
+ }());
329
+ exports.default = LearningMobilenetImage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learning_model",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "learning model develop",
5
5
  "main": "dist/index.bundle.js",
6
6
  "types": "dist/types/index.d.ts",
package/tsconfig.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "compilerOptions": {
3
3
  "target": "ES5",
4
4
  "module": "CommonJS",
5
- "outDir": "dist",
5
+ "outDir": "build",
6
6
  "strict": true,
7
7
  "esModuleInterop": true,
8
8
  "declarationDir": "./types",
@@ -1,252 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
- Object.defineProperty(exports, "__esModule", { value: true });
35
- const tf = __importStar(require("@tensorflow/tfjs"));
36
- class LearningImage {
37
- constructor({ epochs = 10, batchSize = 16, limitSize = 2, } = {}) {
38
- this.trainImages = [];
39
- this.MOBILE_NET_INPUT_WIDTH = 224;
40
- this.MOBILE_NET_INPUT_HEIGHT = 224;
41
- this.MOBILE_NET_INPUT_CHANNEL = 3;
42
- this.IMAGE_NORMALIZATION_FACTOR = 255.0;
43
- this.onProgress = () => { };
44
- this.onLoss = () => { };
45
- this.onTrainBegin = () => { };
46
- this.onTrainEnd = () => { };
47
- this.model = null;
48
- this.epochs = epochs;
49
- this.batchSize = batchSize;
50
- this.labels = [];
51
- this.isRunning = false;
52
- this.isReady = false;
53
- this.limitSize = 2;
54
- }
55
- // 학습 데이타 등록
56
- addData(label, data) {
57
- try {
58
- const tensor = tf.browser.fromPixels(data);
59
- console.log('addData', tensor);
60
- this.trainImages.push(tensor);
61
- this.labels.push(label);
62
- if (this.labels.length >= this.limitSize) {
63
- this.isReady = true;
64
- }
65
- return;
66
- }
67
- catch (error) {
68
- console.error('Model training failed', error);
69
- throw error;
70
- }
71
- }
72
- // 모델 학습 처리
73
- train() {
74
- return __awaiter(this, void 0, void 0, function* () {
75
- if (this.isRunning) {
76
- return Promise.reject(new Error('Training is already in progress.'));
77
- }
78
- // 콜백 정의
79
- const customCallback = {
80
- onTrainBegin: (log) => {
81
- this.onTrainBegin(log);
82
- console.log('Training has started.');
83
- },
84
- onTrainEnd: (log) => {
85
- this.onTrainEnd(log);
86
- console.log('Training has ended.');
87
- this.isRunning = false;
88
- },
89
- onBatchBegin: (batch, logs) => {
90
- console.log(`Batch ${batch} is starting.`);
91
- },
92
- onBatchEnd: (batch, logs) => {
93
- console.log(`Batch ${batch} has ended.`);
94
- },
95
- onEpochBegin: (epoch, logs) => {
96
- //const progress = Math.floor(((epoch + 1) / this.epochs) * 100);
97
- this.onProgress(epoch + 1);
98
- console.log(`Epoch ${epoch + 1} is starting.`);
99
- },
100
- onEpochEnd: (epoch, logs) => {
101
- console.log(`Epoch ${epoch + 1} has ended.`);
102
- this.onLoss(logs.loss);
103
- console.log('Loss:', logs.loss);
104
- }
105
- };
106
- try {
107
- this.isRunning = true;
108
- if (this.labels.length < this.limitSize) {
109
- return Promise.reject(new Error('Please train Data need over 2 data length'));
110
- }
111
- this.model = yield this._createModel(this.labels.length);
112
- const inputData = this._preprocessedInputData(this.model);
113
- const targetData = this._preprocessedTargetData();
114
- const history = yield this.model.fit(inputData, targetData, {
115
- epochs: this.epochs,
116
- batchSize: this.batchSize,
117
- callbacks: customCallback
118
- });
119
- console.log('Model training completed', history);
120
- return history;
121
- }
122
- catch (error) {
123
- this.isRunning = false;
124
- console.error('Model training failed', error);
125
- throw error;
126
- }
127
- });
128
- }
129
- // 추론하기
130
- infer(data) {
131
- return __awaiter(this, void 0, void 0, function* () {
132
- if (this.model === null) {
133
- throw new Error('Model is null');
134
- }
135
- try {
136
- const tensor = tf.browser.fromPixels(data);
137
- const resizedTensor = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
138
- const reshapedTensor = resizedTensor.expandDims(0); // 배치 크기 1을 추가하여 4차원으로 변환
139
- const predictions = this.model.predict(reshapedTensor);
140
- const predictionsData = yield predictions.data(); // 예측 텐서의 데이터를 비동기로 가져옴
141
- const classProbabilities = new Map(); // 클래스별 확률 누적값을 저장할 맵
142
- for (let i = 0; i < predictionsData.length; i++) {
143
- const className = this.labels[i]; // 클래스 이름
144
- const probability = predictionsData[i];
145
- const existingProbability = classProbabilities.get(className);
146
- if (existingProbability !== undefined) {
147
- classProbabilities.set(className, existingProbability + probability);
148
- }
149
- else {
150
- classProbabilities.set(className, probability);
151
- }
152
- }
153
- console.log('Class Probabilities:', classProbabilities);
154
- return classProbabilities;
155
- }
156
- catch (error) {
157
- throw error;
158
- }
159
- });
160
- }
161
- // 모델 저장
162
- saveModel() {
163
- console.log('saved model');
164
- }
165
- // 진행중 여부
166
- running() {
167
- return this.isRunning;
168
- }
169
- ready() {
170
- return this.isReady;
171
- }
172
- // target 라벨 데이타
173
- _preprocessedTargetData() {
174
- // 라벨 unique 처리 & 배열 리턴
175
- console.log('uniqueLabels.length', this.labels, this.labels.length);
176
- const labelIndices = this.labels.map((label) => this.labels.indexOf(label));
177
- console.log('labelIndices', labelIndices);
178
- const oneHotEncode = tf.oneHot(tf.tensor1d(labelIndices, 'int32'), this.labels.length);
179
- console.log('oneHotEncode', oneHotEncode);
180
- return oneHotEncode;
181
- }
182
- // 입력 이미지 데이타
183
- _preprocessedInputData(model) {
184
- // 이미지 배열을 배치로 변환 - [null, 224, 224, 3]
185
- const inputShape = model.inputs[0].shape;
186
- console.log('inputShape', inputShape);
187
- // inputShape를 이와 같이 포멧 맞춘다. for reshape to [224, 224, 3]
188
- const inputShapeArray = inputShape.slice(1);
189
- console.log('inputShapeArray', inputShapeArray);
190
- const inputBatch = tf.stack(this.trainImages.map((image) => {
191
- // 이미지 전처리 및 크기 조정 등을 수행한 후에
192
- // 모델의 입력 형태로 변환하여 반환
193
- const xs = this._preprocessData(image); // 전처리 함수는 사용자 정의해야 함
194
- return tf.reshape(xs, inputShapeArray);
195
- }));
196
- return inputBatch;
197
- }
198
- // 모델 학습하기 위한 데이타 전처리 단계
199
- _preprocessData(tensor) {
200
- try {
201
- // mobilenet model summary를 하면 위와 같이 224,224 사이즈의 입력값 설정되어 있다. ex) input_1 (InputLayer) [null,224,224,3]
202
- const resizedImage = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
203
- // 이미지를 [0,1] 범위로 정규화 255로 나뉜 픽셀값
204
- const normalizedImage = resizedImage.div(this.IMAGE_NORMALIZATION_FACTOR);
205
- // expandDims(0)을 하여 차원을 추가하여 4D텐서 반환
206
- return normalizedImage.expandDims(0);
207
- }
208
- catch (error) {
209
- console.error('Failed to _preprocessData data', error);
210
- throw error;
211
- }
212
- }
213
- // 모델 저장
214
- _createModel(numClasses) {
215
- return __awaiter(this, void 0, void 0, function* () {
216
- try {
217
- const inputShape = [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT, this.MOBILE_NET_INPUT_CHANNEL];
218
- const model = tf.sequential();
219
- model.add(tf.layers.conv2d({
220
- inputShape,
221
- filters: 32,
222
- kernelSize: 3,
223
- activation: 'relu'
224
- }));
225
- model.add(tf.layers.maxPooling2d({ poolSize: 2 }));
226
- model.add(tf.layers.conv2d({
227
- filters: 64,
228
- kernelSize: 3,
229
- activation: 'relu'
230
- }));
231
- model.add(tf.layers.maxPooling2d({ poolSize: 2 }));
232
- model.add(tf.layers.flatten());
233
- model.add(tf.layers.dense({
234
- units: numClasses,
235
- activation: 'softmax'
236
- }));
237
- model.compile({
238
- loss: (numClasses === 2) ? 'binaryCrossentropy' : 'categoricalCrossentropy',
239
- optimizer: tf.train.adam(),
240
- metrics: ['accuracy']
241
- });
242
- model.summary();
243
- return model;
244
- }
245
- catch (error) {
246
- console.error('Failed to load model', error);
247
- throw error;
248
- }
249
- });
250
- }
251
- }
252
- exports.default = LearningImage;
@@ -1,256 +0,0 @@
1
- "use strict";
2
- ///////////////////////////////////////////////////////////////////////////
3
- ///////////////////////////////////////////////////////////////////////////
4
- ///////////////////////////////////////////////////////////////////////////
5
- // mobilenet 모델을 이용한 전이학습 방법
6
- ///////////////////////////////////////////////////////////////////////////
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || function (mod) {
24
- if (mod && mod.__esModule) return mod;
25
- var result = {};
26
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
27
- __setModuleDefault(result, mod);
28
- return result;
29
- };
30
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
31
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
32
- return new (P || (P = Promise))(function (resolve, reject) {
33
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
34
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
35
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
36
- step((generator = generator.apply(thisArg, _arguments || [])).next());
37
- });
38
- };
39
- Object.defineProperty(exports, "__esModule", { value: true });
40
- const tf = __importStar(require("@tensorflow/tfjs"));
41
- class LearningMobilenetImage {
42
- constructor({ modelURL = 'https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json', // 디폴트 mobilenet 이미지
43
- epochs = 10, batchSize = 16, limitSize = 2, } = {}) {
44
- this.trainImages = [];
45
- this.MOBILE_NET_INPUT_WIDTH = 224;
46
- this.MOBILE_NET_INPUT_HEIGHT = 224;
47
- this.MOBILE_NET_INPUT_CHANNEL = 3;
48
- this.IMAGE_NORMALIZATION_FACTOR = 255.0;
49
- // 진행 상태를 나타내는 이벤트를 정의합니다.
50
- this.onProgress = () => { };
51
- this.onLoss = () => { };
52
- this.onTrainBegin = () => { };
53
- this.onTrainEnd = () => { };
54
- this.model = null;
55
- this.epochs = epochs;
56
- this.batchSize = batchSize;
57
- this.labels = [];
58
- this.modelURL = modelURL;
59
- this.isRunning = false;
60
- this.isReady = false;
61
- this.limitSize = limitSize;
62
- }
63
- // 학습 데이타 등록
64
- addData(label, data) {
65
- try {
66
- const tensor = tf.browser.fromPixels(data);
67
- console.log('addData', tensor);
68
- this.trainImages.push(tensor);
69
- this.labels.push(label);
70
- if (this.labels.length >= this.limitSize) {
71
- this.isReady = true;
72
- }
73
- return;
74
- }
75
- catch (error) {
76
- console.error('Model training failed', error);
77
- throw error;
78
- }
79
- }
80
- // 모델 학습 처리
81
- train() {
82
- return __awaiter(this, void 0, void 0, function* () {
83
- if (this.isRunning) {
84
- return Promise.reject(new Error('Training is already in progress.'));
85
- }
86
- // 콜백 정의
87
- const customCallback = {
88
- onTrainBegin: (log) => {
89
- this.onTrainBegin(log);
90
- console.log('Training has started.');
91
- },
92
- onTrainEnd: (log) => {
93
- this.onTrainEnd(log);
94
- console.log('Training has ended.');
95
- this.isRunning = false;
96
- },
97
- onBatchBegin: (batch, logs) => {
98
- console.log(`Batch ${batch} is starting.`);
99
- },
100
- onBatchEnd: (batch, logs) => {
101
- console.log(`Batch ${batch} has ended.`);
102
- },
103
- onEpochBegin: (epoch, logs) => {
104
- //const progress = Math.floor(((epoch + 1) / this.epochs) * 100);
105
- this.onProgress(epoch + 1);
106
- console.log(`Epoch ${epoch + 1} is starting.`);
107
- },
108
- onEpochEnd: (epoch, logs) => {
109
- console.log(`Epoch ${epoch + 1} has ended.`);
110
- console.log('Loss:', logs.loss);
111
- }
112
- };
113
- try {
114
- this.isRunning = true;
115
- if (this.labels.length < this.limitSize) {
116
- return Promise.reject(new Error('Please train Data need over 2 data length'));
117
- }
118
- this.model = yield this._createModel(this.labels.length);
119
- const inputData = this._preprocessedInputData(this.model);
120
- const targetData = this._preprocessedTargetData();
121
- const history = yield this.model.fit(inputData, targetData, {
122
- epochs: this.epochs,
123
- batchSize: this.batchSize,
124
- callbacks: customCallback
125
- });
126
- console.log('Model training completed', history);
127
- return history;
128
- }
129
- catch (error) {
130
- this.isRunning = false;
131
- console.error('Model training failed', error);
132
- throw error;
133
- }
134
- });
135
- }
136
- // 추론하기
137
- infer(data) {
138
- return __awaiter(this, void 0, void 0, function* () {
139
- if (this.model === null) {
140
- throw new Error('Model is null');
141
- }
142
- try {
143
- const tensor = tf.browser.fromPixels(data);
144
- const resizedTensor = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
145
- const reshapedTensor = resizedTensor.expandDims(0); // 배치 크기 1을 추가하여 4차원으로 변환
146
- const predictions = this.model.predict(reshapedTensor);
147
- const predictionsData = yield predictions.data(); // 예측 텐서의 데이터를 비동기로 가져옴
148
- const classProbabilities = new Map(); // 클래스별 확률 누적값을 저장할 맵
149
- for (let i = 0; i < predictionsData.length; i++) {
150
- const className = this.labels[i]; // 클래스 이름
151
- const probability = predictionsData[i];
152
- const existingProbability = classProbabilities.get(className);
153
- if (existingProbability !== undefined) {
154
- classProbabilities.set(className, existingProbability + probability);
155
- }
156
- else {
157
- classProbabilities.set(className, probability);
158
- }
159
- }
160
- console.log('Class Probabilities:', classProbabilities);
161
- return classProbabilities;
162
- }
163
- catch (error) {
164
- throw error;
165
- }
166
- });
167
- }
168
- // 모델 저장
169
- saveModel() {
170
- console.log('saved model');
171
- }
172
- // 진행중 여부
173
- running() {
174
- return this.isRunning;
175
- }
176
- ready() {
177
- return this.isReady;
178
- }
179
- // target 라벨 데이타
180
- _preprocessedTargetData() {
181
- // 라벨 unique 처리 & 배열 리턴
182
- console.log('uniqueLabels.length', this.labels, this.labels.length);
183
- const labelIndices = this.labels.map((label) => this.labels.indexOf(label));
184
- console.log('labelIndices', labelIndices);
185
- const oneHotEncode = tf.oneHot(tf.tensor1d(labelIndices, 'int32'), this.labels.length);
186
- console.log('oneHotEncode', oneHotEncode);
187
- return oneHotEncode;
188
- }
189
- // 입력 이미지 데이타
190
- _preprocessedInputData(model) {
191
- // 이미지 배열을 배치로 변환 - [null, 224, 224, 3]
192
- const inputShape = model.inputs[0].shape;
193
- console.log('inputShape', inputShape);
194
- // inputShape를 이와 같이 포멧 맞춘다. for reshape to [224, 224, 3]
195
- const inputShapeArray = inputShape.slice(1);
196
- console.log('inputShapeArray', inputShapeArray);
197
- const inputBatch = tf.stack(this.trainImages.map((image) => {
198
- // 이미지 전처리 및 크기 조정 등을 수행한 후에
199
- // 모델의 입력 형태로 변환하여 반환
200
- const xs = this._preprocessData(image); // 전처리 함수는 사용자 정의해야 함
201
- return tf.reshape(xs, inputShapeArray);
202
- }));
203
- return inputBatch;
204
- }
205
- // 모델 학습하기 위한 데이타 전처리 단계
206
- _preprocessData(tensor) {
207
- try {
208
- // mobilenet model summary를 하면 위와 같이 224,224 사이즈의 입력값 설정되어 있다. ex) input_1 (InputLayer) [null,224,224,3]
209
- const resizedImage = tf.image.resizeBilinear(tensor, [this.MOBILE_NET_INPUT_WIDTH, this.MOBILE_NET_INPUT_HEIGHT]);
210
- // 이미지를 [0,1] 범위로 정규화 255로 나뉜 픽셀값
211
- const normalizedImage = resizedImage.div(this.IMAGE_NORMALIZATION_FACTOR);
212
- // expandDims(0)을 하여 차원을 추가하여 4D텐서 반환
213
- return normalizedImage.expandDims(0);
214
- }
215
- catch (error) {
216
- console.error('Failed to _preprocessData data', error);
217
- throw error;
218
- }
219
- }
220
- // 모델 저장
221
- _createModel(numClasses) {
222
- return __awaiter(this, void 0, void 0, function* () {
223
- try {
224
- const load_model = yield tf.loadLayersModel(this.modelURL);
225
- // 기존 MobileNet 모델에서 마지막 레이어 제외
226
- const truncatedModel = tf.model({
227
- inputs: load_model.inputs,
228
- outputs: load_model.layers[load_model.layers.length - 2].output
229
- });
230
- // 모델을 학습 가능하게 설정하고 선택한 레이어까지 고정
231
- for (let layer of truncatedModel.layers) {
232
- layer.trainable = false;
233
- }
234
- const model = tf.sequential();
235
- model.add(truncatedModel);
236
- model.add(tf.layers.flatten()); // 필요한 경우 Flatten 레이어 추가
237
- model.add(tf.layers.dense({
238
- units: numClasses,
239
- activation: 'softmax'
240
- }));
241
- model.compile({
242
- loss: (numClasses === 2) ? 'binaryCrossentropy' : 'categoricalCrossentropy',
243
- optimizer: tf.train.adam(),
244
- metrics: ['accuracy']
245
- });
246
- model.summary();
247
- return model;
248
- }
249
- catch (error) {
250
- console.error('Failed to load model', error);
251
- throw error;
252
- }
253
- });
254
- }
255
- }
256
- exports.default = LearningMobilenetImage;
File without changes