- 딥러닝 모델을 서로 다른 프레임워크 간에 서로 옮길 수 있도록 하는 오픈 소스 프로젝트입니다. ONNX는 모델을 중간 계층 형식으로 변환하고 모델을 실행하는 데 필요한 라이브러리를 제공합니다. - 간략히 말해, 다양한 플랫폼 환경(Java, JS, C, C#, C++)에서 환경에 제약 없이 구현된 ‘ML 모델’을 호출하고 수행하여 수행 결과값을 반환받는 것을 의미합니다.
💡 ONNX Runtime 이란?
- ONNX 모델을 실행하기 위한 엔진입니다. ONNX 모델을 실행하기 위해 ONNX 런타임은 빠른 추론을 위한 최적화된 커널을 사용합니다. - 또한, ONNX Runtime은 CPU, GPU 및 딥러닝 가속기(DNNL, NNAPI, OpenVINO)를 지원합니다.따라서, ONNX Runtime은 ONNX 모델을 실행하기 위한 최적화된 런타임 환경을 제공합니다.
2) ONNX(Open Neurl Network Exchange) 메모리 누수 증상 확인
1. 증상 확인
💡 증상 확인
- ONNX 모델을 사용하는것 자체 내에서도 메모리가 누수가 된다는 점을 확인하였습니다.
- 아래와 같이 일반적으로 모델 파일 불러오기 > 모델 로드 > State 공간에 넣기 > 모델 실행이라는 일반적인 과정만을 생각하고 수행했습니다. 해당 과정이 일반적인 수행 과정이고 공식사이트에서도 제안하는 과정이였습니다.
- 그러나 이 과정에서 장기간 수행을 하였을때, 메모리가 누수가 됨을 확인하였습니다
// 모델 파일 불러오기
const HS_EMOTION_MODEL = require("../../../assets/models/hsemotion_q.ort");
// 모델 파일 로드
let _hemotionModel: InferenceSession | null = null;
const _hemotionAssets = await Asset.loadAsync(HS_EMOTION_MODEL);
const _hemotionOnnxModelUri: string | null = _hemotionAssets[0].localUri;
if (_hemotionOnnxModelUri !== null) {
await InferenceSession.create(_hemotionOnnxModelUri)
.then((_loadSession: InferenceSession | null) => {
console.log("[+] Load Hsemotion Model....");
_hemotionModel = _loadSession;
_loadSession = null;
})
.catch((error) => {
console.error(`[-] hsemotion Load Error: ${error}`);
});
}
// 모델 및 State 공간에 넣기(재활용 목적)
setInitModel({
isLoading: true,
isTensorReady: _tensorReady,
faceMeshModel: _faceMeshModel,
hsemotionModel: _hemotionModel,
fsanetModel: _fsanetModel,
hPoseModel: _hposeModel,
});
// 모델 실행
await initModel.hPoseModel
.run(feed, initModel.hPoseModel.outputNames)
.then((fetches: InferenceSession.OnnxValueMapType | null) => {
if (fetches) {
resultScore = result;
}
})
.catch((err) => {
console.error(`_hposeModel.run() 함수에서 에러 발생: ${err}`);
});
💡 반복적으로 ONNX Run()을 수행하였을때 메모리가 증가가 됨을 확인하고, 아래와 같이 여러번 반복해서 Onnx 모델을 수행하는 과정을 하였을때, 메모리가 급격히 증가하는 문제를 확인하였습니다.
시간 별 시점
메모리 사용량(누적)
증감
앱 최초 시점의 메모리
550MB
학습 시작 및 모델로드
1.1GB
10분 소요
1.4GB
300MB
20분 소요
2GB
500MB
30분 소요
2.6GB
600MB
40분 소요
3.2GB
600MB
60분 소요
2. 증상 테스트
💡 증상 테스트
- 실제로 ONNX 모델을 수행하는것 자체에서 메모리가 증감되는지 여부를 확인하기 위해서 반복적으로 수행되는 루프 내에서 강제로 모델을 3번 수행을 하여서 테스트를 수행하였습니다.
const _configTensor2 = tf.zeros([1, 64, 64, 3]).dataSync() as Float32Array;
// [STEP8] 컨버팅한 데이터를 기반으로 모델을 수행합니다.
const feed = { "input": new Tensor(_configTensor2, [1, 64, 64, 3]) }
// 1
if (initModel.fsanetModel) {
const fetches: any = await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
.then((fetches: any) => {
console.log("결과값 1111 :: ", fetches)
})
.catch((err) => console.error(`modelCalcHandler.fsanetEstimate() 함수에서 에러가 발생하였습니다 : ${err}`));
} else console.error("[+] FSA-NET 모델이 존재하지 않습니다 ")
// 2
if (initModel.fsanetModel) {
const fetches: any = await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
.then((fetches: any) => {
console.log("결과값 2222 :: ", fetches)
})
.catch((err) => console.error(`modelCalcHandler.fsanetEstimate() 함수에서 에러가 발생하였습니다 : ${err}`));
} else console.error("[+] FSA-NET 모델이 존재하지 않습니다 ")
// 3
if (initModel.fsanetModel) {
const fetches: any = await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
await initModel.fsanetModel.run(feed, initModel.fsanetModel.outputNames)
.then((fetches: any) => {
console.log("결과값 33333 :: ", fetches)
})
.catch((err) => console.error(`modelCalcHandler.fsanetEstimate() 함수에서 에러가 발생하였습니다 : ${err}`));
} else console.error("[+] FSA-NET 모델이 존재하지 않습니다 ")
💡 각각 모든 모델에 대해서 아래와 같이 메모리가 증가됨을 측정되었습니다.
테스트 케이스
메모리 증가 (시간별)
사전/사후 과정 임의의 값 대입, FSA_NET_MODEL 모델만 수행
300MB 증가(60분 수행)
사전/사후 과정 임의의 값 대입, HS_EMOTION_MODEL 모델만 수행
500MB 증가(60분 수행)
사전/사후 과정 임의의 값 대입, HPOSE_MODEL 모델만 수행
200MB 증가(60분 수행)
사전/사후 과정 임의의 값 대입, PFLD_MODEL, IRIS_LANDMARK_MODEL 모델만 수행