개요

 


 

1. npm install 해야 할 최소한의 모듈

<!-- 둘이 set-->
npm install @tensorflow-models/hand-pose-detection --legacy-peer-deps
npm install @mediapipe/hands --legacy-peer-deps

<!-- 위의 핵심 라이브러리를 실행할 수 있도록 하기 위한 peer dependency-->
npm install @tensorflow/tfjs --legacy-peer-deps
npm install @tensorflow/tfjs-converter --legacy-peer-deps
npm install @tensorflow/tfjs-core --legacy-peer-deps
  • 추가적으로 필요하다고 여겨지는 @tensorflow/tfjs-backend-webgl @tensorflow/tfjs-backend-wasm @tensorflow/tfjs-backend-webgpu 은 이하 peer dependency 에 이미 포함되어 있음
  • 특히 webgl을 tf.setBackend(’webgl’) 식으로 설정할 필요도 없음. 아래 설치하고 핵심 라이브러리를 require 하면 자동 설정됨
  • 주의할 점 : peer dependency 충돌 해결하는 글 보러 가기

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