learning_model 1.0.5 → 1.0.14
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/README.md +15 -1
- package/dist/index.bundle.js +1 -1
- package/dist/types/learning/base.d.ts +3 -5
- package/dist/types/learning/image.d.ts +5 -2
- package/dist/types/learning/mobilenet_image.d.ts +5 -2
- package/dist/types/learning/mobilenet_image.test.d.ts +1 -0
- package/dist/types/public/index.d.ts +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/learning/base.d.ts +19 -0
- package/dist/types/src/learning/image.d.ts +40 -0
- package/dist/types/src/learning/mobilenet_image.d.ts +42 -0
- package/package.json +5 -2
- package/public/index.ts +4 -4
- package/src/learning/base.ts +15 -11
- package/src/learning/image.ts +18 -9
- package/src/learning/mobilenet_image.test.ts +63 -0
- package/src/learning/mobilenet_image.ts +18 -11
- package/tsconfig.json +5 -3
- package/types/learning/base.d.ts +3 -5
- package/types/learning/image.d.ts +5 -2
- package/types/learning/mobilenet_image.d.ts +5 -2
- package/types/learning/mobilenet_image.test.d.ts +1 -0
- package/types/src/learning/base.d.ts +3 -5
- package/types/src/learning/image.d.ts +5 -2
- package/types/src/learning/mobilenet_image.d.ts +5 -2
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as tf from '@tensorflow/tfjs';
|
|
2
2
|
interface LearningInterface {
|
|
3
3
|
model: tf.LayersModel | null;
|
|
4
|
-
epochs: number;
|
|
5
|
-
batchSize: number;
|
|
6
4
|
labels: string[];
|
|
7
5
|
isRunning: boolean;
|
|
8
6
|
isReady: boolean;
|
|
9
|
-
limitSize: number;
|
|
10
7
|
onProgress(progress: number): void;
|
|
11
8
|
onLoss(loss: number): void;
|
|
9
|
+
onEvents(logs: any): void;
|
|
12
10
|
onTrainBegin(log: any): void;
|
|
13
11
|
onTrainEnd(log: any): void;
|
|
14
|
-
addData(label: string, data: any): void
|
|
12
|
+
addData(label: string, data: any): Promise<void>;
|
|
15
13
|
train(): Promise<tf.History>;
|
|
16
|
-
infer(data: any): Promise<
|
|
14
|
+
infer(data: any): Promise<any>;
|
|
17
15
|
saveModel(): void;
|
|
18
16
|
running(): boolean;
|
|
19
17
|
ready(): boolean;
|
|
@@ -4,6 +4,7 @@ declare class LearningImage implements LearningInterface {
|
|
|
4
4
|
model: tf.LayersModel | null;
|
|
5
5
|
epochs: number;
|
|
6
6
|
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
7
8
|
labels: string[];
|
|
8
9
|
isRunning: boolean;
|
|
9
10
|
isReady: boolean;
|
|
@@ -13,17 +14,19 @@ declare class LearningImage implements LearningInterface {
|
|
|
13
14
|
readonly MOBILE_NET_INPUT_HEIGHT = 224;
|
|
14
15
|
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
15
16
|
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
16
|
-
constructor({ epochs, batchSize, limitSize, }?: {
|
|
17
|
+
constructor({ epochs, batchSize, limitSize, learningRate, }?: {
|
|
17
18
|
modelURL?: string;
|
|
18
19
|
epochs?: number;
|
|
19
20
|
batchSize?: number;
|
|
20
21
|
limitSize?: number;
|
|
22
|
+
learningRate?: number;
|
|
21
23
|
});
|
|
22
24
|
onProgress: (progress: number) => void;
|
|
23
25
|
onLoss: (loss: number) => void;
|
|
26
|
+
onEvents: (logs: any) => void;
|
|
24
27
|
onTrainBegin: (log: any) => void;
|
|
25
28
|
onTrainEnd: (log: any) => void;
|
|
26
|
-
addData(label: string, data: any): void
|
|
29
|
+
addData(label: string, data: any): Promise<void>;
|
|
27
30
|
train(): Promise<tf.History>;
|
|
28
31
|
infer(data: any): Promise<Map<string, number>>;
|
|
29
32
|
saveModel(): void;
|
|
@@ -4,6 +4,7 @@ declare class LearningMobilenetImage implements LearningInterface {
|
|
|
4
4
|
model: tf.LayersModel | null;
|
|
5
5
|
epochs: number;
|
|
6
6
|
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
7
8
|
labels: string[];
|
|
8
9
|
modelURL: string;
|
|
9
10
|
isRunning: boolean;
|
|
@@ -15,17 +16,19 @@ declare class LearningMobilenetImage implements LearningInterface {
|
|
|
15
16
|
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
16
17
|
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
17
18
|
constructor({ modelURL, // 디폴트 mobilenet 이미지
|
|
18
|
-
epochs, batchSize, limitSize, }?: {
|
|
19
|
+
epochs, batchSize, limitSize, learningRate, }?: {
|
|
19
20
|
modelURL?: string;
|
|
20
21
|
epochs?: number;
|
|
21
22
|
batchSize?: number;
|
|
22
23
|
limitSize?: number;
|
|
24
|
+
learningRate?: number;
|
|
23
25
|
});
|
|
24
26
|
onProgress: (progress: number) => void;
|
|
25
27
|
onLoss: (loss: number) => void;
|
|
28
|
+
onEvents: (logs: any) => void;
|
|
26
29
|
onTrainBegin: (log: any) => void;
|
|
27
30
|
onTrainEnd: (log: any) => void;
|
|
28
|
-
addData(label: string, data: any): void
|
|
31
|
+
addData(label: string, data: any): Promise<void>;
|
|
29
32
|
train(): Promise<tf.History>;
|
|
30
33
|
infer(data: any): Promise<Map<string, number>>;
|
|
31
34
|
saveModel(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
interface LearningInterface {
|
|
3
|
+
model: tf.LayersModel | null;
|
|
4
|
+
labels: string[];
|
|
5
|
+
isRunning: boolean;
|
|
6
|
+
isReady: boolean;
|
|
7
|
+
onProgress(progress: number): void;
|
|
8
|
+
onLoss(loss: number): void;
|
|
9
|
+
onEvents(logs: any): void;
|
|
10
|
+
onTrainBegin(log: any): void;
|
|
11
|
+
onTrainEnd(log: any): void;
|
|
12
|
+
addData(label: string, data: any): Promise<void>;
|
|
13
|
+
train(): Promise<tf.History>;
|
|
14
|
+
infer(data: any): Promise<any>;
|
|
15
|
+
saveModel(): void;
|
|
16
|
+
running(): boolean;
|
|
17
|
+
ready(): boolean;
|
|
18
|
+
}
|
|
19
|
+
export default LearningInterface;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import LearningInterface from './base';
|
|
3
|
+
declare class LearningImage implements LearningInterface {
|
|
4
|
+
model: tf.LayersModel | null;
|
|
5
|
+
epochs: number;
|
|
6
|
+
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
8
|
+
labels: string[];
|
|
9
|
+
isRunning: boolean;
|
|
10
|
+
isReady: boolean;
|
|
11
|
+
limitSize: number;
|
|
12
|
+
trainImages: tf.Tensor3D[];
|
|
13
|
+
readonly MOBILE_NET_INPUT_WIDTH = 224;
|
|
14
|
+
readonly MOBILE_NET_INPUT_HEIGHT = 224;
|
|
15
|
+
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
16
|
+
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
17
|
+
constructor({ epochs, batchSize, limitSize, learningRate, }?: {
|
|
18
|
+
modelURL?: string;
|
|
19
|
+
epochs?: number;
|
|
20
|
+
batchSize?: number;
|
|
21
|
+
limitSize?: number;
|
|
22
|
+
learningRate?: number;
|
|
23
|
+
});
|
|
24
|
+
onProgress: (progress: number) => void;
|
|
25
|
+
onLoss: (loss: number) => void;
|
|
26
|
+
onEvents: (logs: any) => void;
|
|
27
|
+
onTrainBegin: (log: any) => void;
|
|
28
|
+
onTrainEnd: (log: any) => void;
|
|
29
|
+
addData(label: string, data: any): Promise<void>;
|
|
30
|
+
train(): Promise<tf.History>;
|
|
31
|
+
infer(data: any): Promise<Map<string, number>>;
|
|
32
|
+
saveModel(): void;
|
|
33
|
+
running(): boolean;
|
|
34
|
+
ready(): boolean;
|
|
35
|
+
private _preprocessedTargetData;
|
|
36
|
+
private _preprocessedInputData;
|
|
37
|
+
private _preprocessData;
|
|
38
|
+
private _createModel;
|
|
39
|
+
}
|
|
40
|
+
export default LearningImage;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import LearningInterface from './base';
|
|
3
|
+
declare class LearningMobilenetImage implements LearningInterface {
|
|
4
|
+
model: tf.LayersModel | null;
|
|
5
|
+
epochs: number;
|
|
6
|
+
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
8
|
+
labels: string[];
|
|
9
|
+
modelURL: string;
|
|
10
|
+
isRunning: boolean;
|
|
11
|
+
isReady: boolean;
|
|
12
|
+
limitSize: number;
|
|
13
|
+
trainImages: tf.Tensor3D[];
|
|
14
|
+
readonly MOBILE_NET_INPUT_WIDTH = 224;
|
|
15
|
+
readonly MOBILE_NET_INPUT_HEIGHT = 224;
|
|
16
|
+
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
17
|
+
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
18
|
+
constructor({ modelURL, // 디폴트 mobilenet 이미지
|
|
19
|
+
epochs, batchSize, limitSize, learningRate, }?: {
|
|
20
|
+
modelURL?: string;
|
|
21
|
+
epochs?: number;
|
|
22
|
+
batchSize?: number;
|
|
23
|
+
limitSize?: number;
|
|
24
|
+
learningRate?: number;
|
|
25
|
+
});
|
|
26
|
+
onProgress: (progress: number) => void;
|
|
27
|
+
onLoss: (loss: number) => void;
|
|
28
|
+
onEvents: (logs: any) => void;
|
|
29
|
+
onTrainBegin: (log: any) => void;
|
|
30
|
+
onTrainEnd: (log: any) => void;
|
|
31
|
+
addData(label: string, data: any): Promise<void>;
|
|
32
|
+
train(): Promise<tf.History>;
|
|
33
|
+
infer(data: any): Promise<Map<string, number>>;
|
|
34
|
+
saveModel(): void;
|
|
35
|
+
running(): boolean;
|
|
36
|
+
ready(): boolean;
|
|
37
|
+
private _preprocessedTargetData;
|
|
38
|
+
private _preprocessedInputData;
|
|
39
|
+
private _preprocessData;
|
|
40
|
+
private _createModel;
|
|
41
|
+
}
|
|
42
|
+
export default LearningMobilenetImage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "learning_model",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "learning model develop",
|
|
5
5
|
"main": "dist/index.bundle.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"start": "webpack serve --open --mode development",
|
|
9
9
|
"watch": "webpack --watch --mode=development",
|
|
10
10
|
"build": "tsc && webpack",
|
|
11
|
-
"tsc": "tsc"
|
|
11
|
+
"tsc": "tsc",
|
|
12
|
+
"test": "jest"
|
|
12
13
|
},
|
|
13
14
|
"keywords": [
|
|
14
15
|
"tensorflow"
|
|
@@ -20,11 +21,13 @@
|
|
|
20
21
|
"@tensorflow/tfjs": "^4.6.0",
|
|
21
22
|
"@tensorflow/tfjs-layers": "^4.6.0",
|
|
22
23
|
"canvas": "^2.11.2",
|
|
24
|
+
"jest": "^29.5.0",
|
|
23
25
|
"learning_model": "^1.0.0"
|
|
24
26
|
},
|
|
25
27
|
"devDependencies": {
|
|
26
28
|
"@babel/core": "^7.15.0",
|
|
27
29
|
"@babel/preset-env": "^7.15.0",
|
|
30
|
+
"@types/jest": "^29.5.2",
|
|
28
31
|
"babel-loader": "^8.2.2",
|
|
29
32
|
"clean-webpack-plugin": "^4.0.0",
|
|
30
33
|
"copy-webpack-plugin": "^11.0.0",
|
package/public/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import
|
|
2
|
+
import LearningMobilenetImage from '../src/learning/mobilenet_image';
|
|
3
3
|
|
|
4
4
|
// 프로그레스 바를 표시하는 클래스
|
|
5
5
|
class StatusBar {
|
|
@@ -12,9 +12,9 @@ class StatusBar {
|
|
|
12
12
|
|
|
13
13
|
async function appRun() {
|
|
14
14
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
15
|
-
const learningImage = new
|
|
16
|
-
epochs:
|
|
17
|
-
batchSize:
|
|
15
|
+
const learningImage = new LearningMobilenetImage({
|
|
16
|
+
epochs: 10,
|
|
17
|
+
batchSize: 4
|
|
18
18
|
});
|
|
19
19
|
learningImage.onProgress = (progress: number) => {
|
|
20
20
|
const element = document.getElementById('progress-bar');
|
package/src/learning/base.ts
CHANGED
|
@@ -4,18 +4,12 @@ import * as tf from '@tensorflow/tfjs';
|
|
|
4
4
|
interface LearningInterface {
|
|
5
5
|
// tf 모델
|
|
6
6
|
model: tf.LayersModel | null;
|
|
7
|
-
// epoch 훈련 개수
|
|
8
|
-
epochs: number;
|
|
9
|
-
// 배치 사이즈
|
|
10
|
-
batchSize: number;
|
|
11
7
|
// 라벨 array
|
|
12
8
|
labels: string[];
|
|
13
9
|
// 이미 train중인지 여부 - 이미 진행중인경우 취소
|
|
14
10
|
isRunning: boolean;
|
|
15
11
|
// 준비
|
|
16
12
|
isReady: boolean;
|
|
17
|
-
// 데이타 사이즈
|
|
18
|
-
limitSize: number;
|
|
19
13
|
|
|
20
14
|
// 진행상태 이벤트
|
|
21
15
|
onProgress(progress: number): void;
|
|
@@ -23,21 +17,31 @@ interface LearningInterface {
|
|
|
23
17
|
// loss 정보
|
|
24
18
|
onLoss(loss: number): void;
|
|
25
19
|
|
|
20
|
+
// 이벤트 로그 정보
|
|
21
|
+
onEvents(logs: any): void;
|
|
22
|
+
|
|
23
|
+
// 학습 시작 이벤트
|
|
26
24
|
onTrainBegin(log: any): void;
|
|
27
25
|
|
|
26
|
+
// 학습 종료 이벤트
|
|
28
27
|
onTrainEnd(log: any): void;
|
|
29
28
|
|
|
30
29
|
// 학습 데이타 추가 data는 다른 클래스에 맞게 any로 설정
|
|
31
|
-
addData(label: string, data: any): void
|
|
30
|
+
addData(label: string, data: any): Promise<void>;
|
|
31
|
+
|
|
32
32
|
// 학습 진행
|
|
33
33
|
train(): Promise<tf.History>;
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
|
|
35
|
+
// 추론하기
|
|
36
|
+
infer(data: any): Promise<any>;
|
|
37
|
+
|
|
36
38
|
// 모델 저장
|
|
37
39
|
saveModel(): void;
|
|
38
|
-
|
|
40
|
+
|
|
41
|
+
// 학습 진행 여부
|
|
39
42
|
running(): boolean;
|
|
40
|
-
|
|
43
|
+
|
|
44
|
+
// 모델 준비 여부
|
|
41
45
|
ready(): boolean;
|
|
42
46
|
}
|
|
43
47
|
|
package/src/learning/image.ts
CHANGED
|
@@ -5,6 +5,7 @@ class LearningImage implements LearningInterface {
|
|
|
5
5
|
model: tf.LayersModel | null;
|
|
6
6
|
epochs: number;
|
|
7
7
|
batchSize: number;
|
|
8
|
+
learningRate: number;
|
|
8
9
|
labels: string[];
|
|
9
10
|
isRunning: boolean;
|
|
10
11
|
isReady: boolean;
|
|
@@ -20,10 +21,12 @@ class LearningImage implements LearningInterface {
|
|
|
20
21
|
epochs = 10,
|
|
21
22
|
batchSize = 16,
|
|
22
23
|
limitSize = 2,
|
|
23
|
-
|
|
24
|
+
learningRate = 0.001,
|
|
25
|
+
}: { modelURL?: string, epochs?: number, batchSize?: number, limitSize?: number, learningRate?: number} = {}) {
|
|
24
26
|
this.model = null;
|
|
25
27
|
this.epochs = epochs;
|
|
26
28
|
this.batchSize = batchSize;
|
|
29
|
+
this.learningRate = learningRate;
|
|
27
30
|
this.labels = [];
|
|
28
31
|
this.isRunning = false;
|
|
29
32
|
this.isReady = false;
|
|
@@ -34,12 +37,14 @@ class LearningImage implements LearningInterface {
|
|
|
34
37
|
|
|
35
38
|
public onLoss: (loss: number) => void = () => {};
|
|
36
39
|
|
|
40
|
+
public onEvents: (logs: any) => void = () => {};
|
|
41
|
+
|
|
37
42
|
public onTrainBegin: (log: any) => void = () => {};
|
|
38
43
|
|
|
39
44
|
public onTrainEnd: (log: any) => void = () => {};
|
|
40
45
|
|
|
41
46
|
// 학습 데이타 등록
|
|
42
|
-
public addData(label: string, data: any): void {
|
|
47
|
+
public async addData(label: string, data: any): Promise<void> {
|
|
43
48
|
try {
|
|
44
49
|
const tensor = tf.browser.fromPixels(data);
|
|
45
50
|
console.log('addData', tensor);
|
|
@@ -48,7 +53,7 @@ class LearningImage implements LearningInterface {
|
|
|
48
53
|
if(this.labels.length >= this.limitSize) {
|
|
49
54
|
this.isReady = true;
|
|
50
55
|
}
|
|
51
|
-
return;
|
|
56
|
+
return Promise.resolve();
|
|
52
57
|
} catch (error) {
|
|
53
58
|
console.error('Model training failed', error);
|
|
54
59
|
throw error;
|
|
@@ -79,14 +84,14 @@ class LearningImage implements LearningInterface {
|
|
|
79
84
|
console.log(`Batch ${batch} has ended.`);
|
|
80
85
|
},
|
|
81
86
|
onEpochBegin: (epoch: number, logs: any) => {
|
|
82
|
-
|
|
83
|
-
this.onProgress(epoch+1);
|
|
84
|
-
console.log(`Epoch ${epoch+1} is starting.`);
|
|
87
|
+
console.log(`Epoch ${epoch+1} is starting.`, logs);
|
|
85
88
|
},
|
|
86
89
|
onEpochEnd: (epoch: number, logs: any) => {
|
|
87
90
|
console.log(`Epoch ${epoch+1} has ended.`);
|
|
88
91
|
this.onLoss(logs.loss);
|
|
89
|
-
console.log('Loss:', logs
|
|
92
|
+
console.log('Loss:', logs);
|
|
93
|
+
this.onEvents(logs);
|
|
94
|
+
this.onProgress(epoch+1);
|
|
90
95
|
}
|
|
91
96
|
};
|
|
92
97
|
|
|
@@ -97,7 +102,9 @@ class LearningImage implements LearningInterface {
|
|
|
97
102
|
}
|
|
98
103
|
this.model = await this._createModel(this.labels.length);
|
|
99
104
|
const inputData = this._preprocessedInputData(this.model);
|
|
105
|
+
console.log('inputData', inputData);
|
|
100
106
|
const targetData = this._preprocessedTargetData();
|
|
107
|
+
console.log('targetData', targetData);
|
|
101
108
|
const history = await this.model.fit(inputData, targetData, {
|
|
102
109
|
epochs: this.epochs,
|
|
103
110
|
batchSize: this.batchSize,
|
|
@@ -229,10 +236,12 @@ class LearningImage implements LearningInterface {
|
|
|
229
236
|
units: numClasses,
|
|
230
237
|
activation: 'softmax'
|
|
231
238
|
}));
|
|
239
|
+
|
|
240
|
+
const optimizer = tf.train.adam(this.learningRate); // Optimizer를 생성하고 학습률을 설정합니다.
|
|
232
241
|
model.compile({
|
|
233
242
|
loss: (numClasses === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy',
|
|
234
|
-
optimizer:
|
|
235
|
-
metrics: ['accuracy']
|
|
243
|
+
optimizer: optimizer,
|
|
244
|
+
metrics: ['accuracy', 'acc']
|
|
236
245
|
});
|
|
237
246
|
model.summary();
|
|
238
247
|
return model;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as tf from '@tensorflow/tfjs';
|
|
4
|
+
import LearningMobilenetImage from './mobilenet_image';
|
|
5
|
+
|
|
6
|
+
describe('LearningMobilenetImage', () => {
|
|
7
|
+
let learning: LearningMobilenetImage;
|
|
8
|
+
const imagePath = path.join(__dirname, 'data/images/image.jpg'); // 이미지 파일 경로
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
learning = new LearningMobilenetImage({});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
// 테스트 후에 모델 저장
|
|
16
|
+
learning.saveModel();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should add data', () => {
|
|
20
|
+
const label = 'label1';
|
|
21
|
+
const image = fs.readFileSync(imagePath); // 이미지 파일 읽어오기
|
|
22
|
+
|
|
23
|
+
learning.addData(label, image);
|
|
24
|
+
|
|
25
|
+
expect(learning.labels).toContain(label);
|
|
26
|
+
expect(learning.trainImages.length).toBe(1);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should not train if data is insufficient', async () => {
|
|
30
|
+
spyOn(console, 'error'); // 콘솔 에러 로그를 가로채기 위해 spyOn 사용
|
|
31
|
+
|
|
32
|
+
const result = await learning.train().catch((error: any) => error);
|
|
33
|
+
|
|
34
|
+
expect(result).toBeInstanceOf(Error);
|
|
35
|
+
expect(result.message).toBe('Please train Data need over 2 data length');
|
|
36
|
+
expect(console.error).toHaveBeenCalledWith('Model training failed', result);
|
|
37
|
+
expect(learning.model).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should train and infer', async () => {
|
|
41
|
+
const label1 = 'label1';
|
|
42
|
+
const image1 = fs.readFileSync(imagePath); // 이미지 파일 읽어오기
|
|
43
|
+
const label2 = 'label2';
|
|
44
|
+
const image2 = fs.readFileSync(imagePath); // 이미지 파일 읽어오기
|
|
45
|
+
const testData = fs.readFileSync(imagePath); // 테스트 데이터 읽어오기
|
|
46
|
+
|
|
47
|
+
learning.addData(label1, image1);
|
|
48
|
+
learning.addData(label2, image2);
|
|
49
|
+
|
|
50
|
+
const history = await learning.train();
|
|
51
|
+
|
|
52
|
+
expect(history).toBeDefined();
|
|
53
|
+
expect(learning.model).toBeInstanceOf(tf.LayersModel);
|
|
54
|
+
expect(learning.isRunning).toBe(false);
|
|
55
|
+
|
|
56
|
+
const predictions = await learning.infer(testData);
|
|
57
|
+
|
|
58
|
+
expect(predictions).toBeInstanceOf(Map);
|
|
59
|
+
expect(predictions.size).toBe(2);
|
|
60
|
+
// expect(predictions.get(label1)).toBeCloseTo(/* 예측값 */);
|
|
61
|
+
// expect(predictions.get(label2)).toBeCloseTo(/* 예측값 */);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -11,6 +11,7 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
11
11
|
model: tf.LayersModel | null;
|
|
12
12
|
epochs: number;
|
|
13
13
|
batchSize: number;
|
|
14
|
+
learningRate: number;
|
|
14
15
|
labels: string[];
|
|
15
16
|
modelURL: string;
|
|
16
17
|
isRunning: boolean;
|
|
@@ -28,10 +29,12 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
28
29
|
epochs = 10,
|
|
29
30
|
batchSize = 16,
|
|
30
31
|
limitSize = 2,
|
|
31
|
-
|
|
32
|
+
learningRate = 0.001,
|
|
33
|
+
}: { modelURL?: string, epochs?: number, batchSize?: number, limitSize?: number, learningRate?: number} = {}) {
|
|
32
34
|
this.model = null;
|
|
33
35
|
this.epochs = epochs;
|
|
34
36
|
this.batchSize = batchSize;
|
|
37
|
+
this.learningRate = learningRate;
|
|
35
38
|
this.labels = [];
|
|
36
39
|
this.modelURL = modelURL;
|
|
37
40
|
this.isRunning = false;
|
|
@@ -44,12 +47,14 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
44
47
|
|
|
45
48
|
public onLoss: (loss: number) => void = () => {};
|
|
46
49
|
|
|
50
|
+
public onEvents: (logs: any) => void = () => {};
|
|
51
|
+
|
|
47
52
|
public onTrainBegin: (log: any) => void = () => {};
|
|
48
53
|
|
|
49
54
|
public onTrainEnd: (log: any) => void = () => {};
|
|
50
55
|
|
|
51
56
|
// 학습 데이타 등록
|
|
52
|
-
public addData(label: string, data: any): void {
|
|
57
|
+
public async addData(label: string, data: any): Promise<void> {
|
|
53
58
|
try {
|
|
54
59
|
const tensor = tf.browser.fromPixels(data);
|
|
55
60
|
console.log('addData', tensor);
|
|
@@ -58,7 +63,7 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
58
63
|
if(this.labels.length >= this.limitSize) {
|
|
59
64
|
this.isReady = true;
|
|
60
65
|
}
|
|
61
|
-
return;
|
|
66
|
+
return Promise.resolve();
|
|
62
67
|
} catch (error) {
|
|
63
68
|
console.error('Model training failed', error);
|
|
64
69
|
throw error;
|
|
@@ -89,13 +94,14 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
89
94
|
console.log(`Batch ${batch} has ended.`);
|
|
90
95
|
},
|
|
91
96
|
onEpochBegin: (epoch: number, logs: any) => {
|
|
92
|
-
|
|
93
|
-
this.onProgress(epoch+1);
|
|
94
|
-
console.log(`Epoch ${epoch+1} is starting.`);
|
|
97
|
+
console.log(`Epoch ${epoch+1} is starting.`, logs);
|
|
95
98
|
},
|
|
96
99
|
onEpochEnd: (epoch: number, logs: any) => {
|
|
97
100
|
console.log(`Epoch ${epoch+1} has ended.`);
|
|
98
|
-
console.log('Loss:', logs
|
|
101
|
+
console.log('Loss:', logs);
|
|
102
|
+
this.onLoss(logs.loss);
|
|
103
|
+
this.onProgress(epoch+1);
|
|
104
|
+
this.onEvents(logs);
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
|
|
@@ -124,7 +130,7 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
124
130
|
// 추론하기
|
|
125
131
|
public async infer(data: any): Promise<Map<string, number>> {
|
|
126
132
|
if (this.model === null) {
|
|
127
|
-
|
|
133
|
+
return Promise.reject(new Error('Model is Null'));
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
try {
|
|
@@ -234,11 +240,12 @@ class LearningMobilenetImage implements LearningInterface {
|
|
|
234
240
|
units: numClasses,
|
|
235
241
|
activation: 'softmax'
|
|
236
242
|
}));
|
|
237
|
-
|
|
243
|
+
|
|
244
|
+
const optimizer = tf.train.adam(this.learningRate); // Optimizer를 생성하고 학습률을 설정합니다.
|
|
238
245
|
model.compile({
|
|
239
246
|
loss: (numClasses === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy',
|
|
240
|
-
optimizer:
|
|
241
|
-
metrics: ['accuracy']
|
|
247
|
+
optimizer: optimizer,
|
|
248
|
+
metrics: ['accuracy', 'acc']
|
|
242
249
|
});
|
|
243
250
|
model.summary();
|
|
244
251
|
return model;
|
package/tsconfig.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
3
|
+
"target": "ES5",
|
|
4
4
|
"module": "CommonJS",
|
|
5
|
-
"outDir": "
|
|
5
|
+
"outDir": "dist",
|
|
6
6
|
"strict": true,
|
|
7
7
|
"esModuleInterop": true,
|
|
8
8
|
"declarationDir": "./types",
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"src/**/*.ts"
|
|
14
14
|
],
|
|
15
15
|
"exclude": [
|
|
16
|
-
"node_modules"
|
|
16
|
+
"node_modules",
|
|
17
|
+
"src/**/*.test.ts",
|
|
18
|
+
"dist"
|
|
17
19
|
]
|
|
18
20
|
}
|
package/types/learning/base.d.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as tf from '@tensorflow/tfjs';
|
|
2
2
|
interface LearningInterface {
|
|
3
3
|
model: tf.LayersModel | null;
|
|
4
|
-
epochs: number;
|
|
5
|
-
batchSize: number;
|
|
6
4
|
labels: string[];
|
|
7
5
|
isRunning: boolean;
|
|
8
6
|
isReady: boolean;
|
|
9
|
-
limitSize: number;
|
|
10
7
|
onProgress(progress: number): void;
|
|
11
8
|
onLoss(loss: number): void;
|
|
9
|
+
onEvents(logs: any): void;
|
|
12
10
|
onTrainBegin(log: any): void;
|
|
13
11
|
onTrainEnd(log: any): void;
|
|
14
|
-
addData(label: string, data: any): void
|
|
12
|
+
addData(label: string, data: any): Promise<void>;
|
|
15
13
|
train(): Promise<tf.History>;
|
|
16
|
-
infer(data: any): Promise<
|
|
14
|
+
infer(data: any): Promise<any>;
|
|
17
15
|
saveModel(): void;
|
|
18
16
|
running(): boolean;
|
|
19
17
|
ready(): boolean;
|
|
@@ -4,6 +4,7 @@ declare class LearningImage implements LearningInterface {
|
|
|
4
4
|
model: tf.LayersModel | null;
|
|
5
5
|
epochs: number;
|
|
6
6
|
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
7
8
|
labels: string[];
|
|
8
9
|
isRunning: boolean;
|
|
9
10
|
isReady: boolean;
|
|
@@ -13,17 +14,19 @@ declare class LearningImage implements LearningInterface {
|
|
|
13
14
|
readonly MOBILE_NET_INPUT_HEIGHT = 224;
|
|
14
15
|
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
15
16
|
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
16
|
-
constructor({ epochs, batchSize, limitSize, }?: {
|
|
17
|
+
constructor({ epochs, batchSize, limitSize, learningRate, }?: {
|
|
17
18
|
modelURL?: string;
|
|
18
19
|
epochs?: number;
|
|
19
20
|
batchSize?: number;
|
|
20
21
|
limitSize?: number;
|
|
22
|
+
learningRate?: number;
|
|
21
23
|
});
|
|
22
24
|
onProgress: (progress: number) => void;
|
|
23
25
|
onLoss: (loss: number) => void;
|
|
26
|
+
onEvents: (logs: any) => void;
|
|
24
27
|
onTrainBegin: (log: any) => void;
|
|
25
28
|
onTrainEnd: (log: any) => void;
|
|
26
|
-
addData(label: string, data: any): void
|
|
29
|
+
addData(label: string, data: any): Promise<void>;
|
|
27
30
|
train(): Promise<tf.History>;
|
|
28
31
|
infer(data: any): Promise<Map<string, number>>;
|
|
29
32
|
saveModel(): void;
|
|
@@ -4,6 +4,7 @@ declare class LearningMobilenetImage implements LearningInterface {
|
|
|
4
4
|
model: tf.LayersModel | null;
|
|
5
5
|
epochs: number;
|
|
6
6
|
batchSize: number;
|
|
7
|
+
learningRate: number;
|
|
7
8
|
labels: string[];
|
|
8
9
|
modelURL: string;
|
|
9
10
|
isRunning: boolean;
|
|
@@ -15,17 +16,19 @@ declare class LearningMobilenetImage implements LearningInterface {
|
|
|
15
16
|
readonly MOBILE_NET_INPUT_CHANNEL = 3;
|
|
16
17
|
readonly IMAGE_NORMALIZATION_FACTOR = 255;
|
|
17
18
|
constructor({ modelURL, // 디폴트 mobilenet 이미지
|
|
18
|
-
epochs, batchSize, limitSize, }?: {
|
|
19
|
+
epochs, batchSize, limitSize, learningRate, }?: {
|
|
19
20
|
modelURL?: string;
|
|
20
21
|
epochs?: number;
|
|
21
22
|
batchSize?: number;
|
|
22
23
|
limitSize?: number;
|
|
24
|
+
learningRate?: number;
|
|
23
25
|
});
|
|
24
26
|
onProgress: (progress: number) => void;
|
|
25
27
|
onLoss: (loss: number) => void;
|
|
28
|
+
onEvents: (logs: any) => void;
|
|
26
29
|
onTrainBegin: (log: any) => void;
|
|
27
30
|
onTrainEnd: (log: any) => void;
|
|
28
|
-
addData(label: string, data: any): void
|
|
31
|
+
addData(label: string, data: any): Promise<void>;
|
|
29
32
|
train(): Promise<tf.History>;
|
|
30
33
|
infer(data: any): Promise<Map<string, number>>;
|
|
31
34
|
saveModel(): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|