learning_model 1.0.51 → 1.0.53
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/Makefile +7 -1
- package/dist/learning/mobilenet.d.ts +0 -1
- package/dist/learning/mobilenet.js +3 -3
- package/dist/lib/index.js +10 -0
- package/dist/lib/learning/base.js +2 -0
- package/dist/lib/learning/data_model.js +215 -0
- package/dist/lib/learning/data_model.test.js +56 -0
- package/dist/lib/learning/mobilenet.d.ts +0 -1
- package/dist/lib/learning/mobilenet.js +386 -0
- package/dist/lib/learning/mobilenet.test.js +89 -0
- package/dist/lib/utils/canvas.js +46 -0
- package/dist/lib/utils/data_manager.d.ts +2 -3
- package/dist/lib/utils/data_manager.js +72 -0
- package/dist/lib/utils/dataset.d.ts +1 -1
- package/dist/lib/utils/dataset.js +20 -0
- package/dist/lib/utils/tf.d.ts +1 -1
- package/dist/lib/utils/tf.js +135 -0
- package/lib/learning/mobilenet.ts +3 -3
- package/package.json +2 -3
- package/tsconfig.json +1 -0
- package/dist/index.bundle.js +0 -2
- package/dist/index.bundle.js.LICENSE.txt +0 -352
- package/dist/index.html +0 -1
package/Makefile
CHANGED
|
@@ -38,7 +38,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
38
38
|
};
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
const tf = __importStar(require("@tensorflow/tfjs"));
|
|
41
|
-
require("@tensorflow/tfjs-backend-wasm");
|
|
41
|
+
const tfjs_backend_wasm_1 = require("@tensorflow/tfjs-backend-wasm");
|
|
42
42
|
const tfjs_1 = require("@tensorflow/tfjs");
|
|
43
43
|
const tf_1 = require("../utils/tf");
|
|
44
44
|
const canvas_1 = require("../utils/canvas");
|
|
@@ -187,8 +187,6 @@ class LearningMobilenet {
|
|
|
187
187
|
return __awaiter(this, void 0, void 0, function* () {
|
|
188
188
|
const isWasmSupported = yield this.checkWasmSupport();
|
|
189
189
|
if (isWasmSupported) {
|
|
190
|
-
yield tf.setBackend('wasm');
|
|
191
|
-
yield tf.ready();
|
|
192
190
|
console.log('Backend is set to WebAssembly');
|
|
193
191
|
}
|
|
194
192
|
else {
|
|
@@ -201,6 +199,8 @@ class LearningMobilenet {
|
|
|
201
199
|
checkWasmSupport() {
|
|
202
200
|
return __awaiter(this, void 0, void 0, function* () {
|
|
203
201
|
try {
|
|
202
|
+
const wasmVersion = tf.version['tfjs-backend-wasm'] || tf.version['tfjs-core'];
|
|
203
|
+
(0, tfjs_backend_wasm_1.setWasmPaths)(`https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@${wasmVersion}/dist/`);
|
|
204
204
|
yield tf.setBackend('wasm');
|
|
205
205
|
yield tf.ready();
|
|
206
206
|
return true;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DataModel = exports.LearningMobilenet = void 0;
|
|
7
|
+
const mobilenet_1 = __importDefault(require("./learning/mobilenet"));
|
|
8
|
+
exports.LearningMobilenet = mobilenet_1.default;
|
|
9
|
+
const data_model_1 = __importDefault(require("./learning/data_model"));
|
|
10
|
+
exports.DataModel = data_model_1.default;
|
|
@@ -0,0 +1,215 @@
|
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
const tf = __importStar(require("@tensorflow/tfjs"));
|
|
49
|
+
const data_manager_1 = __importDefault(require("../utils/data_manager"));
|
|
50
|
+
class DataModel {
|
|
51
|
+
constructor({ epochs = 50, batchSize = 16, limitSize = 2, learningRate = 0.001, validateRate = 0.15, } = {}) {
|
|
52
|
+
this.model = null;
|
|
53
|
+
this.dataManager = new data_manager_1.default();
|
|
54
|
+
this.epochs = epochs;
|
|
55
|
+
this.batchSize = batchSize;
|
|
56
|
+
this.learningRate = learningRate;
|
|
57
|
+
this.validateRate = validateRate;
|
|
58
|
+
this.isRunning = false;
|
|
59
|
+
this.isReady = false;
|
|
60
|
+
this.isTrainedDone = false;
|
|
61
|
+
this.limitSize = limitSize;
|
|
62
|
+
}
|
|
63
|
+
onProgress(progress) {
|
|
64
|
+
console.log(`Training progress: ${progress}%`);
|
|
65
|
+
}
|
|
66
|
+
onLoss(loss) {
|
|
67
|
+
console.log(`Loss: ${loss}`);
|
|
68
|
+
}
|
|
69
|
+
onTrainBegin(log) {
|
|
70
|
+
console.log('Training started', log);
|
|
71
|
+
}
|
|
72
|
+
onTrainEnd(log) {
|
|
73
|
+
console.log('Training ended', log);
|
|
74
|
+
}
|
|
75
|
+
onEpochEnd(epoch, logs) {
|
|
76
|
+
console.log(`Epoch ${epoch} ended with logs:`, logs);
|
|
77
|
+
}
|
|
78
|
+
addData(label, data) {
|
|
79
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
const normalizedData = this.normalizeData(data);
|
|
81
|
+
const augmentedData = this.augmentData(normalizedData);
|
|
82
|
+
yield this.dataManager.addData(label, augmentedData);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
augmentData(data) {
|
|
86
|
+
return data.map(value => value + (Math.random() * 0.1 - 0.05)); // 약간의 노이즈 추가
|
|
87
|
+
}
|
|
88
|
+
normalizeData(data) {
|
|
89
|
+
const mean = data.reduce((sum, value) => sum + value, 0) / data.length;
|
|
90
|
+
const std = Math.sqrt(data.reduce((sum, value) => sum + Math.pow(value - mean, 2), 0) / data.length);
|
|
91
|
+
return data.map(value => (value - mean) / std);
|
|
92
|
+
}
|
|
93
|
+
train() {
|
|
94
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
if (this.isRunning) {
|
|
96
|
+
throw new Error('Training is already in progress');
|
|
97
|
+
}
|
|
98
|
+
const { xs, ys } = this.dataManager.convertToTensors();
|
|
99
|
+
const jointModel = tf.sequential();
|
|
100
|
+
jointModel.add(tf.layers.dense({
|
|
101
|
+
units: 32,
|
|
102
|
+
activation: 'relu',
|
|
103
|
+
inputShape: [xs.shape[1]],
|
|
104
|
+
kernelRegularizer: tf.regularizers.l2({ l2: 0.01 }) // L2 정규화 추가
|
|
105
|
+
}));
|
|
106
|
+
jointModel.add(tf.layers.dropout({ rate: 0.5 })); // 드롭아웃 레이어 추가
|
|
107
|
+
jointModel.add(tf.layers.dense({
|
|
108
|
+
units: 16,
|
|
109
|
+
activation: 'relu',
|
|
110
|
+
kernelRegularizer: tf.regularizers.l2({ l2: 0.01 }) // L2 정규화 추가
|
|
111
|
+
}));
|
|
112
|
+
jointModel.add(tf.layers.dropout({ rate: 0.5 })); // 드롭아웃 레이어 추가
|
|
113
|
+
jointModel.add(tf.layers.dense({ units: Object.keys(this.dataManager.getLabelMap()).length, activation: 'softmax' }));
|
|
114
|
+
jointModel.compile({
|
|
115
|
+
optimizer: tf.train.adam(this.learningRate), // 학습률 조정
|
|
116
|
+
loss: 'sparseCategoricalCrossentropy',
|
|
117
|
+
metrics: ['accuracy']
|
|
118
|
+
});
|
|
119
|
+
this.model = jointModel;
|
|
120
|
+
this.isRunning = true;
|
|
121
|
+
const history = yield jointModel.fit(xs, ys, {
|
|
122
|
+
epochs: this.epochs,
|
|
123
|
+
batchSize: this.batchSize,
|
|
124
|
+
validationSplit: 0.2,
|
|
125
|
+
callbacks: {
|
|
126
|
+
onEpochEnd: (epoch, logs) => this.onEpochEnd(epoch, logs),
|
|
127
|
+
onBatchEnd: (batch, logs) => {
|
|
128
|
+
},
|
|
129
|
+
onTrainBegin: (logs) => this.onTrainBegin(logs),
|
|
130
|
+
onTrainEnd: (logs) => {
|
|
131
|
+
this.isRunning = false;
|
|
132
|
+
this.isReady = true;
|
|
133
|
+
this.onTrainEnd(logs);
|
|
134
|
+
},
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
return history;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
infer(data) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
if (!this.isReady || !this.model) {
|
|
143
|
+
throw new Error('Model is not ready');
|
|
144
|
+
}
|
|
145
|
+
// 입력 데이터를 텐서로 변환
|
|
146
|
+
const input = tf.tensor2d([data], [1, data.length], 'float32');
|
|
147
|
+
try {
|
|
148
|
+
// 모델 예측 수행
|
|
149
|
+
const prediction = this.model.predict(input);
|
|
150
|
+
// 예측 결과 값들을 추출
|
|
151
|
+
const values = yield prediction.data();
|
|
152
|
+
// 클래스 이름과 확률을 저장할 Map 생성
|
|
153
|
+
const classProbabilities = new Map();
|
|
154
|
+
const EPSILON = 1e-6; // 매우 작은 값을 표현하기 위한 엡실론
|
|
155
|
+
// 라벨 맵을 가져옴
|
|
156
|
+
const labelMap = this.dataManager.getLabelMap();
|
|
157
|
+
const reverseLabelMap = Object.entries(labelMap).reduce((acc, [key, value]) => {
|
|
158
|
+
acc[value] = key;
|
|
159
|
+
return acc;
|
|
160
|
+
}, {});
|
|
161
|
+
// 각 클래스의 확률 계산 및 조정
|
|
162
|
+
for (let i = 0; i < values.length; i++) {
|
|
163
|
+
let probability = Math.max(0, Math.min(1, values[i])); // 확률 값을 0과 1 사이로 조정
|
|
164
|
+
probability = probability < EPSILON ? 0 : probability; // 매우 작은 확률 값을 0으로 간주
|
|
165
|
+
const className = reverseLabelMap[i]; // 클래스 이름을 문자열로 가져옴
|
|
166
|
+
if (className) {
|
|
167
|
+
const existingProbability = classProbabilities.get(className);
|
|
168
|
+
if (existingProbability !== undefined) {
|
|
169
|
+
classProbabilities.set(className, existingProbability + probability);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
classProbabilities.set(className, probability);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// 텐서 해제
|
|
177
|
+
prediction.dispose();
|
|
178
|
+
input.dispose();
|
|
179
|
+
console.log('classProbabilities', classProbabilities);
|
|
180
|
+
return classProbabilities;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
// 텐서 해제
|
|
184
|
+
input.dispose();
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
saveModel(handlerOrURL, config) {
|
|
190
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
191
|
+
if (!this.model) {
|
|
192
|
+
throw new Error('No model to save');
|
|
193
|
+
}
|
|
194
|
+
yield this.model.save(handlerOrURL, config);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
running() {
|
|
198
|
+
return this.isRunning;
|
|
199
|
+
}
|
|
200
|
+
ready() {
|
|
201
|
+
return this.isReady;
|
|
202
|
+
}
|
|
203
|
+
load(_a) {
|
|
204
|
+
return __awaiter(this, arguments, void 0, function* ({ jsonURL, labels }) {
|
|
205
|
+
this.model = yield tf.loadLayersModel(jsonURL);
|
|
206
|
+
this.isReady = true;
|
|
207
|
+
// Assuming the labels are provided in the correct order
|
|
208
|
+
this.dataManager = new data_manager_1.default();
|
|
209
|
+
labels.forEach((label, index) => {
|
|
210
|
+
this.dataManager.getLabelMap()[label] = index;
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
exports.default = DataModel;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const data_model_1 = __importDefault(require("./data_model"));
|
|
16
|
+
describe('LearningModel', () => {
|
|
17
|
+
let learningModel;
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
learningModel = new data_model_1.default({
|
|
20
|
+
epochs: 10,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
it('should add sufficient data and train the model', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
+
// 훈련 데이터 생성
|
|
25
|
+
const numSamples = 2000; // 훈련 데이터 샘플 수
|
|
26
|
+
for (let i = 0; i < numSamples; i++) {
|
|
27
|
+
const classLabel = i % 2 === 0 ? 'class1' : 'class2'; // 두 개의 클래스를 번갈아가며 생성
|
|
28
|
+
const data = Array.from({ length: 3 }, () => Math.random() * 10); // 임의의 데이터 생성
|
|
29
|
+
yield learningModel.addData(classLabel, data);
|
|
30
|
+
}
|
|
31
|
+
// 모델 훈련
|
|
32
|
+
const history = yield learningModel.train();
|
|
33
|
+
// 훈련 결과 평가
|
|
34
|
+
expect(history).toBeDefined();
|
|
35
|
+
expect(learningModel.ready()).toBe(true);
|
|
36
|
+
}), 30000); // 타임아웃을 15초로 설정
|
|
37
|
+
it('should make predictions', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
+
// 훈련 데이터 생성
|
|
39
|
+
const numSamples = 100; // 훈련 데이터 샘플 수
|
|
40
|
+
for (let i = 0; i < numSamples; i++) {
|
|
41
|
+
const classLabel = i % 2 === 0 ? 'class1' : 'class2'; // 두 개의 클래스를 번갈아가며 생성
|
|
42
|
+
const data = Array.from({ length: 3 }, () => Math.random() * 10); // 임의의 데이터 생성
|
|
43
|
+
yield learningModel.addData(classLabel, data);
|
|
44
|
+
yield learningModel.addData(classLabel, [1, 2, 4]);
|
|
45
|
+
}
|
|
46
|
+
// 모델 훈련
|
|
47
|
+
yield learningModel.train();
|
|
48
|
+
// 테스트 데이터 생성
|
|
49
|
+
const testData = [4.0, 3.1, 5.2]; // 테스트할 데이터 생성
|
|
50
|
+
// 예측 수행
|
|
51
|
+
const result = yield learningModel.infer(testData);
|
|
52
|
+
console.log('result', result);
|
|
53
|
+
// 결과 평가
|
|
54
|
+
expect(result).toBeDefined();
|
|
55
|
+
}), 30000);
|
|
56
|
+
});
|