React & React Native/라이브러리 활용

[RN] ONNX(Open Neural Network Exchange) 이해하기 -3 : ONNX 모델 정리

adjh54 2023. 11. 8. 14:22
반응형
해당 글에서는 ONNXRuntime에서 모델을 불러온 이후 페이지를 이동하는 경우 메모리 누수가 발생함에 따라서 ONNX 모델 정리하는 방법에 대해서 공유합니다.





 
 
 

💡 [참고] 해당 글은 이전에 작성한 글들을 기반으로 작성하였습니다.

[RN] ONNX(Open Neural Network Exchange) 이해하기 -1: React Native 활용

해당 글에서는 React Native 기반의 ONNX에 대해서 이해하고 사용 방법에 대해서 확인합니다. 1) ONNX(Open Neural Network Exchange) 💡 ONNX(Open Neural Network Exchange) 란? - 딥러닝 모델을 서로 다른 프레임워크 간

adjh54.tistory.com

 

[RN] ONNX(Open Neural Network Exchange) 이해하기 -2  : ONNX 모델 불러오기

해당 글에서는 React-Native 환경에서 ONNXRuntime을 수행하는 방법에 대해서 확인해 봅니다. 💡 이전에 작성한 글을 읽고 오시면 이해하는데 도움이 됩니다. [RN] ONNX(Open Neural Network Exchange) 이해하기 -1

adjh54.tistory.com

 
 
 
 
 

1) ONNX Runtime 문제점


💡 ONNX Runtime 문제점

- ONNX Runtime을 이용한 프로젝트를 수행하는 도중에 메모리 체크를 하는 시점에 ‘메모리 누수’가 발생하였습니다.

- ONNX Runtime을 이용하여 모델을 불러오고 다른 페이지를 이동하는 경우 해당 모델이 지워지지 않는 점을 발견하게 되어서 이에 관련하여 처리 과정을 확인해 봅니다.

 
 
 

2) ONNX Runtime 문제점 증상 확인


💡 안드로이드 Profiler를 이용하여서 메모리 체크를 수행하였습니다.

 

💡[참고] Profiler 사용 방법에 대해 궁금하시면 아래의 글을 확인하시면 됩니다.

[RN] React-native 환경에서 Android Studio 디버깅 이용방법: Logcat, Profiler, App Inspection

해당 글에서는 React-native 환경에서 Android Studio의 디버깅 툴(Locat, Profiler, App Inspection)을 사용하는 방법에 대해 알아봅니다. 1) Android Studio 디버깅 주요 기능 1. Logcat 💡 Logcat - 안드로이드 스튜디오

adjh54.tistory.com

 
 
 

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;

 
 
 

💡 [참고] 이전에 작성한 글을 참고로 구성하였습니다.

[RN] ONNX(Open Neural Network Exchange) 이해하기 -2  : ONNX 모델 불러오기

해당 글에서는 React-Native 환경에서 ONNXRuntime을 수행하는 방법에 대해서 확인해 봅니다. 💡 이전에 작성한 글을 읽고 오시면 이해하는데 도움이 됩니다. [RN] ONNX(Open Neural Network Exchange) 이해하기 -1

adjh54.tistory.com

 
 

💡 동일한 환경에서 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까지 내려감을 확인하였습니다.(메모리를 회수하였습니다)

 
 
 
 
 
 
오늘도 감사합니다. 😀
 
 

반응형