이하 글은 Tensorflow-models 모델 로딩을 위해 공부하고 문제를 해결한 글입니다.

모델 버전 관리 - 1. model-loading 글 보러 가기 

2023.11.30 - [AI/TensorFlow | Tensorflow.js] - [Tensorflow-models] 모델 버전 관리 - 1. model loading

 


 

문제 인식

  • npm install 
    • npm install @tensorflow/tfjs npm install @tensorflow/tfjs-converter npm install @tensorflow/tfjs-core npm install @tensorflow-models/hand-pose-detection 등을 실행하여 npm 모듈을 설치할 때, peer dependency 충돌이 있으면 다음과 같은 오류가 발생함.
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: @tensorflow-models/mobilenet@2.0.4
npm ERR! Found: @tensorflow/tfjs-converter@4.10.0
npm ERR! node_modules/@tensorflow/tfjs-converter
npm ERR!   peer @tensorflow/tfjs-converter@"^4.9.0" from @tensorflow-models/hand-pose-detection@2.0.1
npm ERR!   node_modules/@tensorflow-models/hand-pose-detection
npm ERR!     @tensorflow-models/hand-pose-detection@"^2.0.1" from the root project
npm ERR!   @tensorflow/tfjs-converter@"4.10.0" from @tensorflow/tfjs@4.10.0
npm ERR!   node_modules/@tensorflow/tfjs
npm ERR!     @tensorflow/tfjs@"^4.10.0" from the root project
npm ERR!   2 more (the root project, @tensorflow-models/face-detection)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @tensorflow/tfjs-converter@"~1.2.1" from @tensorflow-models/mobilenet@2.0.4
npm ERR! node_modules/@tensorflow-models/mobilenet
npm ERR!   @tensorflow-models/mobilenet@"2.0.4" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: @tensorflow/tfjs-converter@1.2.11
npm ERR! node_modules/@tensorflow/tfjs-converter
npm ERR!   peer @tensorflow/tfjs-converter@"~1.2.1" from @tensorflow-models/mobilenet@2.0.4
npm ERR!   node_modules/@tensorflow-models/mobilenet
npm ERR!     @tensorflow-models/mobilenet@"2.0.4" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

 

  • npm 모듈 사용(실행)
    • 특정 모델을 로딩하여 예측 시도할 때 (코드 실행할 때), peer dependency 충돌이 있으면 다음과 같은 오류가 발생하기도 함
    • 그러나 오류 양상이 다양하므로 밑 종류 하나 만으로 한정할 수 없음
    • 따라서 ‘~찾을 수 없음’, ‘resolve 하다가 ~없어서 문제 발생함’ 등의 뉘앙스의 오류는 peer dependency로 인한 오류가 아닐까? 의심해야 함
client:159 ./node_modules/scratch-vm/node_modules/@tensorflow/tfjs-converter/dist/tf-converter.esm.js
Module build failed: Error: ENOENT: no such file or directory, open 'C:\Workspace\Rogic\scratch-gui\node_modules\scratch-vm\node_modules@tensorflow\tfjs-converter\dist\tf-converter.esm.js'

 

  • 주의할 점은 npm install 할 때를 제외하고, peer dependency로 인한 오류임에도 콘솔에서 직접적으로 알려주지 않는다는 것
    • 따라서 두번째 느낌으로 발생한 오류는 궁극적으로 peer dependency로 발생하는 오류일 수 있음을 인지해야 함
    • 여타 오류 해결 방법을 사용하기 전, 후에 peer dependency로 인한 오류가 아닐까? 의심해야 함

 


 

충돌 원인 : 실제 설치된 패키지 버전 ≠ peer dependency에서 요구하는 패키지 버전

  • peer dependency 의미
    • 친구 패키지 명 + 버전
    • 친구 패키지 : 해당 패키지를 정상적으로 활용하기 위해 필요한 다른 패키지
    • 즉 실제로 패키지에서 직접 require(import) 하지는 않더라도 호환성이 필요한 경우 명시하는 것
    • npm 3 버전까지는 peer dependency를 자동으로 설치해줌, 4 - 6 버전에서는 경고 메시지만 띄워줌, 7 버전부터는 peer dependency 버전이 맞지 않으면 설치 불가
      → 나의 npm - -version : 9.6.4 , 그렇다면 실제 설치된 버전 ≠ peer dependency 요구 버전이라면 해당 패키지 아예 설치 및 실행 불가? NO!
      → 해결법은 맨 마지막에!

 

  • peer dependency 확인
    • package-lock.json
"node_modules/@tensorflow-models/hand-pose-detection": {
    "version": "2.0.1",
    "resolved": "https://registry.npmjs.org/@tensorflow-models/hand-pose-detection/-/hand-pose-detection-2.0.1.tgz",
    "integrity": "sha512-zRA+jz2qOq5FMcyqSFxaQyi6T4YNbMbQhd6SQMI791FQ8yYj23kLgYa73g2NssR5AmM/2ATu9Vcjnf7LUrVLOQ==",
    "dependencies": {
        "rimraf": "^3.0.2",
        "tslib": "^2.6.1"
    },
    "peerDependencies": {
        "@mediapipe/hands": "~0.4.0",
        "@tensorflow/tfjs-backend-webgl": "^4.9.0",
        "@tensorflow/tfjs-converter": "^4.9.0",
        "@tensorflow/tfjs-core": "^4.9.0"
    }
  },
  • dependency vs dev dependency
    1. dependency
      • 앱에 종속된 가장 일반적인 종속성
      • 런타임빌드타임개발중 일 때 모두 필요
      • 따라서 앱이 빌드될 때 여기에 속해 있는 패키지들이 모두 번들에 포함되어 배포
    2. dev dependency
      • 빌드타임개발중 일 때 필요
      • 따라서 앱이 빌드될 때 도움 주거나 참조 되지만, 번들에 포함X

 


 

해결법 (요약)

  • npm 7 버전부터는 peer dependency 버전이 맞지 않으면 설치 불가
    • 그러나 무조건 peer dependency에서 얘기하는 딱! 그! 버전만 설치해야 패키지 실행할 수 있다는 것은 말이 안 됨 (사용자마다 프로젝트, pc 상황이 다를 수 있기 때문)
    • 해결 방법은 npm 공식 깃헙 블로그 기준 2가지가 있음 : --force 또는 --legacy-peer-deps
    • 더보기
      You have the option to retry with --force to bypass the conflict or --legacy-peer-deps command to ignore peer dependencies entirely (this behavior is similar to versions 4-6). - in npm github blog
  • 2가지 방법 요약
    --force --legacy-peer-deps
    충돌 우회 충돌 무시
    package-lock.json에 몇 가지의 다른 의존 버전들을 추가 peerDependency가 맞지 않아도 일단 설치
    • 이번 프로젝트에서는 주로 --legacy-peer-deps 를 활용하여 peer dependency 충돌 해결 (사수 제안)
    • 그러나 일반적인 경우 --force--legacy-peer-deps 로 적용해보는 것이 좋을 것 같음
      ( ∵ --force--legacy-peer-deps 보다 덜 강제적)

 


 

이번 프로젝트 (Tensorflow-models 구현) 에서 주의할 점

  • peer dependency를 설치한 경우, 해당 패키지가 실제 실행 가능한지 빌드 + 실행해서 확인 필수
    • 실행해보아야 하는 것 : ①모델 로딩(create detector) ②예측(estimateHands)
  • 해당 프로젝트의 경우 다음과 같은 버전 특징을 갖고 있었음
      hand-pose-detection face-landmarks-detection pose-detection
    peer dependency 권장 버전 "peerDependencies":
    { "@mediapipe/hands": "~0.4.0", "@tensorflow/tfjs-backend-webgl": "^4.9.0", "@tensorflow/tfjs-converter": "^4.9.0", "@tensorflow/tfjs-core": "^4.9.0" }
    "peerDependencies":
    { "@mediapipe/face_detection": "~0.4.0", "@tensorflow/tfjs-backend-webgl": "^4.4.0", "@tensorflow/tfjs-converter": "^4.4.0", "@tensorflow/tfjs-core": "^4.4.0" }
    "peerDependencies":
    { "@mediapipe/pose": "~0.5.0", "@tensorflow/tfjs-backend-wasm": "^4.10.0", "@tensorflow/tfjs-backend-webgl": "^4.10.0", "@tensorflow/tfjs-backend-webgpu": "^4.10.0", "@tensorflow/tfjs-converter": "^4.10.0", "@tensorflow/tfjs-core": "^4.10.0" }
    실제 실행 가능한 최소 버전 ( hand-pose-detection, face-landmarks-detection, pose-detection 최신 버전 기준) - tfjs, tfjs-core, tfjs-converter : ^3.3.0 - tfjs, tfjs-core, tfjs-converter : ^3.3.0 - tfjs, tfjs-core, tfjs-converter : ^4.4.0
    • 위처럼 ①모델마다 실제 실행 가능한 최소 버전 및 권장 peer dependency 버전이 상이하고 ②향후 프로젝트 확장 가능성 고려하여 , 사수님 제안으로 tfjs, tfjs-core, tfjs-converter 모델 모두 4.10.0 으로 설치함
    • tfjs, tfjs-core, tfjs-converter 모델은 2점대 이후로 버전이 한 몸으로 움직임. 따라서 버전업 할 때도 한 번에 했음
    • 위 3개 모델을 제외한 나머지 모델 ( ex. @tensorflow/tfjs-backend-webgl @tensorflow/tfjs-backend-wasm @tensorflow/tfjs-backend-webgpu) 은 위 3개 모델을 설치하면 자동 실행되므로 별도 설치 필요 없음
      ( 사실 @tensorflow/tfjs-converter 도 4점대 이후에는 포함되어있지만 그 전에는 포함되어 있지 않으므로, 보수적으로 별도 설치함)

 

merry가 참고하여 구현한 Tensorflow-models 공식 깃헙 바로가기

이하 모든 내용의 근본 출처는 위의 공식 깃헙임을 밝힙니다.

 


 

문제 인식

  • model loading이 되지 않았음. 
  • 내가 구현 시도한 hand-ppse-detection 모델은 model loading에 해당하는 것이 createDetector 함수인데, 이하에서 설명할 해결하기 위한 노력을 모두 해보아도 밑 오류로 귀결되었음.
  • 이번 글의 문제는 시리즈 2편에서 작성할 peer dependency 까지 모두 이해해야 해결 가능함. 
detector-handpose.js:179 Error in animate: Error: Weight StatefulPartitionedCall/model/conv_handedness/MatMul/ReadVariableOp has unknown quantization dtype float16. 
Supported quantization dtypes are: 'uint8' and 'uint16'.

 


 

1.  createDetector() 함수 구조

export async function createDetector(
    model: SupportedModels,
    modelConfig?: MediaPipeHandsMediaPipeModelConfig | MediaPipeHandsTfjsModelConfig)  // createDetector() 함수의 옵션
  • hand-pose-detection이 아닌 face-landmarks-detection과 pose-detection도 모델 로딩 함수 구조는 거의 동일함
  • createDetector() 함수의 옵션
// MediaPipeHandsTfjsModelConfig
/* `detectorModelUrl`: Optional. An optional string that specifies custom url of
* the detector model. This is useful for area/countries that don't have access
* to the model hosted on tf.hub.
*
* `landmarkModelUrl`: Optional. An optional string that specifies custom url of
* the landmark model. This is useful for area/countries that don't have access
* to the model hosted on tf.hub.
*/

export interface MediaPipeHandsTfjsModelConfig extends MediaPipeHandsModelConfig {
    runtime: 'tfjs';
    detectorModelUrl?: string;
    landmarkModelUrl?: string;
}


// MediaPipeHandsMediaPipeModelConfig
export interface MediaPipeHandsMediaPipeModelConfig extends MediaPipeHandsModelConfig {
      runtime: 'mediapipe';
      solutionPath?: string;
}
  • 이번 프로젝트 특성 상 온라인 환경이 아니어도 (인터넷이 끊어져도) 기능이 정상적으로 작동되어야 했음. 
  • 따라서 tensorflow-hub (최근 kaggle로 이관되었음) 에서 모델을 다운로드 받았음 
  • 그래서 위 옵션들 중 detectorModelUrl, landmarkModelUrl 을 실제 모델이 저장된 위치로 올바르게 설정해야 했음.
  • 그러나 아무리 설정해도 문제 인식에서 제시한 오류만 출력됨

 


 

2. 해결하기 위한 노력

 

1) 오프라인으로 다운로드 받은 모델 구조 파악
  • 전체 모델 구조 = detector + landmark
  • 각 detector, landmark 구조 = json + binary 파일
    1. json
      • 모델의 '구조' 해당하는 부분
      • 모델의 각 레이어에 대한 정보와 레이어가 어떻게 연결되어 있는지에 대한 정보
        →  이 때 레이어는 CNN의 그 layer임
      • 모델의 아키텍처의 모든 정보를 제공 → 이 파일만으로 모델의 구조를 재구성 가능
      • model.json의 weightsManifest 에서 바이너리 파일을 자동 참조
        → 따라서 바이너리 파일을 model.json과 동일한 디렉토리에 저장해놓는다면, 수동으로 경로를 지정할 필요 없음
      •  
      • 더보기
        "weightsManifest": [{"paths": ["group1-shard1of1.bin"],
    2. binary
      • 모델의 '가중치' 에 해당하는 부분 
      • 학습된 파라미터 포함 (ex. 각 레이어의 가중치와 편향 값 등)
      • 모델의 학습 결과를 저장하는 데 사용
      • 따라서 이 파일이 있어야만 학습된 모델을 이용하여 새로운 데이터 예측을 수행 가능
  • 오프라인으로 모델 다운 받은 모습

01

 


 

2) detector, landmark 파일을 각각 Url 옵션에 넣으면 된다고 생각했음
  • 그러나 어떠한 String 형식으로 넣어야 하는지 의문
  • 해결법
    • 힌트 : detectorModelUrl, landmarkModelUrl에 특정 경로를 쓰면, localhost:포트번호/ ~ 이하 경로에서 파일을 찾음
    • 따라서 GUI(빌드 시작점)에서 VM에 저장된 tensorflow-models를 찾을 수 있도록 경로를 작성해주어야 함
      → 이 경로는 webpack.config.js 에 저장되어 있음. 만약 아래 구조로 저장되어있지 않다면 내가 직접 설정해야 함
    • 이하 예시처럼 설정되어 있다면 해결법 내의 Url 설정한 것처럼 설정하여 VM에 저장된 tensorflow-models 파일들에 접근 가능함
plugins: base.plugins.concat([
	new CopyWebpackPlugin({
    patterns: [
			{
        from: 'node_modules/scratch-vm/tensorflow-models',
        to: 'static/tensorflow-models'
    },

 


 

3) model.json과 binary 파일 위치를 url 구조로 바꾸어주는 코드 사용해보기
  • 2) 실행했는데 계속 문제 인식에서 언급한 quantization 오류 발생함 
  • 따라서 이하 제시한 model.json과 binary 파일 위치를 url 구조로 바꾸어주는 코드 사용하기도 했었음
  • 물론 정상적으로 모델 로딩하지 못 하여 문제 인식에서 언급한 quantization 오류 또 발생함 
_bindPage () {
    return new Promise((resolve, reject) => {
        this.knn = knnClassifier.create();
        tf.ready().then(() => {
            // 바이너리 파일을 읽어와 blob -> url중 필요한 값 추출
            let blobList = [];
            const binaryLength = 4
            for (let i = 1; i <= binaryLength; i++) {
                const binary = require(`!!url-loader!./mobilenet-model/group1-shard${i}of${binaryLength}.bin`);
                const blob = this._b64toBlob(binary.default.slice(binary.default.indexOf('base64,') + 7))
                const url = URL.createObjectURL(blob)
                const urlArr = url.split("/");
                blobList.push(urlArr[urlArr.length - 1]);
            }

            // json data를 읽어와서 바이너리 파일 url을 이용한 paths 값 수정
            const modelJson = require('./mobilenet-model/model.json');
            modelJson.weightsManifest[0].paths = blobList;

            const blob = new Blob([JSON.stringify(modelJson)]);
            mobilenetModule.load({
                version: 1,
                alpha: 1.00,
                modelUrl: URL.createObjectURL(blob)
            }).then(module => {
                this.mobilenet = module;
                resolve();
            }).catch(err => {
                this.mobilenet = null;
                this.knn = null;
                console.warn(err)
                reject(err);
            });
        });
    });
}

_b64toBlob (b64Data, contentType = '', sliceSize = 512) {
    const byteCharacters = atob(b64Data);
    const byteArrays = [];

    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        const slice = byteCharacters.slice(offset, offset + sliceSize);

        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        const byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }

    const blob = new Blob(byteArrays, {type: contentType});
    return blob;
}

 


 

해결법 (요약)

  • 이하 2가지가 모두 충족되어야 정상적으로 모델 로딩 (createDetector()) 할 수 있음 
    1. 모델 버전 설정 : hand-pose-detection은 최신 버전 + tfjs는 peer dependency 충돌나도 실제 실행은 되는 버전으로 버전업
      모델 버전 관리 - 2. peer dependency 충돌 글 보러 가기 
           
    2. 옵션 detectorModelUrl, landmarkModelUrl 설정
_createDetector() {
  const model = SupportedModels.MediaPipeHands;
  const detector = createDetector(model, {
      runtime: "tfjs",
      modelType: "lite",
      maxHands: 2, // or 2~10.
      flipHorizontal: false,
      staticImageMode: false,
      detectorModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_detector_lite_1/model.json",
      landmarkModelUrl:
          "/static/tensorflow-models/tfjs-model_handpose_3d_landmark_lite_1/model.json",
  });
  return detector;
}

 

+ Recent posts