React & React Native/라이브러리 활용
[RN] ONNX(Open Neural Network Exchange) 이해하기 -3 : ONNX 모델 정리
adjh54
2023. 11. 8. 14:22
반응형
해당 글에서는 ONNXRuntime에서 모델을 불러온 이후 페이지를 이동하는 경우 메모리 누수가 발생함에 따라서 ONNX 모델 정리하는 방법에 대해서 공유합니다.
💡 [참고] 해당 글은 이전에 작성한 글들을 기반으로 작성하였습니다.
1) ONNX Runtime 문제점
💡 ONNX Runtime 문제점
- ONNX Runtime을 이용한 프로젝트를 수행하는 도중에 메모리 체크를 하는 시점에 ‘메모리 누수’가 발생하였습니다.
- ONNX Runtime을 이용하여 모델을 불러오고 다른 페이지를 이동하는 경우 해당 모델이 지워지지 않는 점을 발견하게 되어서 이에 관련하여 처리 과정을 확인해 봅니다.
2) ONNX Runtime 문제점 증상 확인
💡 안드로이드 Profiler를 이용하여서 메모리 체크를 수행하였습니다.
💡[참고] Profiler 사용 방법에 대해 궁금하시면 아래의 글을 확인하시면 됩니다.
1. 문제점 증상 확인
💡 ONNX 호출 시점의 메모리 확인
- 최초 840MB의 메모리가 최대 1.1GB까지 호출시점에 올라가는 것을 확인할 수 있습니다.
💡 ONNX 호출 이후 해당 페이지를 벗어났을 경우의 메모리 확인
- ONNX 모델을 벗어나서 다른 페이지로 이동하는 경우 '메모리가 회수'가 되어야 하는데, 적용하기 이전에는 메모리가 모두 반환되지 않음을 확인하였습니다. (메모리 누수 발생)
3) ONNX Runtime 모델 정리하기
💡 ONNX 모델 정리
- 사용한 모델에 대한 ‘메모리 누수’를 막기 위해 정리하는 작업을 수행합니다.
1. 적용 예시
💡 사용 예시
- 최초 화면이 로드될 때 useEffect() 함수를 통해서 ONNX 모델을 로드해 오고 State 공간에 저장을 합니다.
- 해당 State 공간에서 저장한 내용은 화면 내에서 연산처리를 할 때 사용하기 위해 구성하였습니다.
import { InferenceSession, Tensor } from "onnxruntime-react-native";
import React, { useEffect, useState } from 'react';
import { Text, TouchableHighlight, View } from 'react-native';
import { Asset } from "expo-asset";
type modelInfo = {
aModel: InferenceSession | null,
bModel: InferenceSession | null,
cModel: InferenceSession | null
}
const OnnxScreen = ({ route, navigation, appState }) => {
const [onnxModel, setOnnxModel] = useState<modelInfo>({
aModel: null,
bModel: null,
cModel: null
});
useEffect(() => {
loadModel();
}, []);
/**
* 최초 모델을 불러옵니다.
*/
const loadModel = async () => {
// [STEP1] aModel Load : ONNX
let _aModel: InferenceSession | null = null;
const _aAssets = await Asset.loadAsync(A_MODEL);
const _aOnnxModelUri: string | null = _aAssets[0].localUri;
if (_aOnnxModelUri !== null) {
await InferenceSession.create(_aOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load A Model....");
_aModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] A Model Load Error: ${error}`); });
}
// [STEP2] bModel Load : ONNX
let _bModel: InferenceSession | null = null;
const _bAssets = await Asset.loadAsync(B_MODEL)
const _bOnnxModelUri: string | null = _bAssets[0].localUri;
if (_bOnnxModelUri !== null) {
await InferenceSession.create(_bOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load B Model Model....");
_bModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] B Model Load Error: ${error}`); });
}
// [STEP3] cModel Load : ONNX
let _cModel: InferenceSession | null = null;
const _cAssets = await Asset.loadAsync(C_MODEL);
const _cOnnxModelUri: string | null = _cAssets[0].localUri;
if (_cOnnxModelUri !== null) {
await InferenceSession.create(_cOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load C Model Model....");
_cModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] C Model Load Error: ${error}`); });
}
setOnnxModel({
aModel: _aModel,
bModel: _bModel,
cModel: _cModel
})
}
/**
* home으로 이동합니다.
*/
const onPressHome = () => {
navigation.navigate("home")
}
return (
<View>
<TouchableHighlight onPress={onPressHome}>
<Text>다음 페이지로 이동</Text>
</TouchableHighlight>
</View>
)
}
export default OnnxScreen;
💡 [참고] 이전에 작성한 글을 참고로 구성하였습니다.
💡 동일한 환경에서 ONNX 호출 시점의 메모리 확인
- 시작 페이지에서 대략 770MB의 메모리로 시작을 하였습니다. 그리고 ONNX 모델을 수행하였을 때 최대 1.2GB까지 올라감을 확인하였습니다.
2. 모델 정리: InferenceSession.release()
💡 InferenceSession.release()
- 인퍼런스 세션을 해제하는 데 사용되는 함수입니다. 이 함수를 호출하면 인퍼런스 세션의 리소스가 해제되어 메모리 누수를 방지할 수 있습니다. 인퍼런스 세션이 더 이상 필요하지 않을 때 이 함수를 호출하여 세션을 명시적으로 해제할 수 있습니다.
- 세션을 해제하면 해당 세션에 할당된 리소스가 해제되므로 메모리 사용량을 줄일 수 있습니다. 또한, 세션을 해제하면 세션 관련된 모든 상태와 컨텍스트가 초기화되므로 다른 작업에 사용할 수 있는 상태로 돌아갑니다.
if (onnxModel.aModel) onnxModel.aModel.release();
if (onnxModel.bModel) onnxModel.bModel.release();
if (onnxModel.cModel) onnxModel.cModel.release();
💡예시
- 해당 페이지 내에서 aModel, bModel, cModel을 State 공간에 저장을 하고 있습니다.
- 또한 해당 페이지 내에서 연산처리를 할 예정입니다.
- 모든 연산처리가 끝나고 home으로 이동을 하게 되면 useEffect()가 cleanUp으로 aModel, bModel, cModel을 모두 제거해 주고 마무리합니다.
import { InferenceSession, Tensor } from "onnxruntime-react-native";
import React, { useEffect, useState } from 'react';
import { Text, TouchableHighlight, View } from 'react-native';
import { Asset } from "expo-asset";
type modelInfo = {
aModel: InferenceSession | null,
bModel: InferenceSession | null,
cModel: InferenceSession | null
}
const OnnxScreen = ({ route, navigation, appState }) => {
const [onnxModel, setOnnxModel] = useState<modelInfo>({
aModel: null,
bModel: null,
cModel: null
});
useEffect(() => {
loadModel();
return () => {
if (onnxModel.aModel) onnxModel.aModel.release();
if (onnxModel.bModel) onnxModel.bModel.release();
if (onnxModel.cModel) onnxModel.cModel.release();
}
}, []);
/**
* 최초 모델을 불러옵니다.
*/
const loadModel = async () => {
// [STEP4] hsemotion Model Load : ONNX
let _aModel: InferenceSession | null = null;
const _aAssets = await Asset.loadAsync(A_MODEL);
const _aOnnxModelUri: string | null = _aAssets[0].localUri;
if (_aOnnxModelUri !== null) {
await InferenceSession.create(_aOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load A Model....");
_aModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] A Model Load Error: ${error}`); });
}
// [STEP5] FSA-NET Model Load : ONNX
let _bModel: InferenceSession | null = null;
const _bAssets = await Asset.loadAsync(B_MODEL)
const _bOnnxModelUri: string | null = _bAssets[0].localUri;
if (_bOnnxModelUri !== null) {
await InferenceSession.create(_bOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load B Model Model....");
_bModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] B Model Load Error: ${error}`); });
}
// [STEP6] hPose Model Load : ONNX
let _cModel: InferenceSession | null = null;
const _cAssets = await Asset.loadAsync(C_MODEL);
const _cOnnxModelUri: string | null = _cAssets[0].localUri;
if (_cOnnxModelUri !== null) {
await InferenceSession.create(_cOnnxModelUri)
.then((_loadSession: InferenceSession) => {
console.log("[+] Load C Model Model....");
_cModel = _loadSession;
})
.catch((error) => { throw new Error(`[-] C Model Load Error: ${error}`); });
}
setOnnxModel({
aModel: _aModel,
bModel: _bModel,
cModel: _cModel
})
}
/**
* home으로 이동합니다.
*/
const onPressHome = () => {
navigation.navigate("home")
}
return (
<View>
<TouchableHighlight onPress={onPressHome}>
<Text>다음 페이지로 이동</Text>
</TouchableHighlight>
</View>
)
}
export default OnnxScreen;
💡 소스코드를 적용한 이후 페이지를 벗어난 경우
- ONNX 모델을 수행한 이후 1.2GB까지 올라갔던 메모리가 해당 소스코드를 적용하여 정상적으로 회수되어 740MB까지 내려감을 확인하였습니다.(메모리를 회수하였습니다)
오늘도 감사합니다. 😀
반응형