TensorFlow.js: TensorFlow.js로 전이 학습을 사용하여 나만의 'Teachable Machine' 만들기

1. 시작하기 전에

지난 몇 년간 TensorFlow.js 모델 사용이 기하급수적으로 증가한 이제 많은 자바스크립트 개발자가 기존의 고유한 모델을 가져와 고유한 맞춤 데이터로 작동하도록 다시 학습시키고 있습니다. 업계 기존 모델을 가져와서 기본 모델이라고도 하며, 서로 유사하지만 유사 도메인에서 사용하는 모델을 전이 학습이라고 합니다.

전이 학습은 완전히 빈 모델보다 많은 이점이 있습니다. 이전에 학습된 모델에서 이미 배운 지식을 다시 사용할 수 있으며, 분류하려는 새 항목의 예시가 적어집니다. 또한 전체 네트워크 대신 모델 아키텍처의 마지막 몇 개 레이어만 재학습해야 하므로 학습이 훨씬 빨라지는 경우가 많습니다. 따라서 전이 학습은 실행 기기에 따라 리소스가 다를 수 있지만 쉽게 데이터를 가져올 수 있도록 센서에 직접 액세스할 수 있는 웹브라우저 환경에 매우 적합합니다.

이 Codelab에서는 빈 캔버스에서 웹 앱을 빌드하고 Google의 인기 'Teachable Machine' 웹사이트를 다시 만드는 방법을 보여줍니다. 웹사이트를 사용하면 모든 사용자가 웹캠에서 몇 가지 예시 이미지만으로 맞춤 객체를 인식하는 데 사용할 수 있는 작동하는 웹 앱을 만들 수 있습니다. 이 웹사이트는 이 Codelab의 머신러닝 측면에 집중할 수 있도록 의도적으로 최소화되어 있습니다. 그러나 기존의 Teachable Machine 웹사이트와 마찬가지로 UX를 개선하기 위해 기존 웹 개발자 환경을 적용할 수 있는 범위도 많습니다.

기본 요건

이 Codelab은 TensorFlow.js 사전 제작 모델 및 기본 API 사용에 어느 정도 익숙하며 TensorFlow.js에서 전이 학습을 시작하려는 웹 개발자를 대상으로 합니다.

  • 이 실습에서는 TensorFlow.js, HTML5, CSS 및 자바스크립트에 대한 기본 지식을 갖추고 있다고 가정합니다.

Tensorflow.js를 처음 사용하는 경우 먼저 무료 0에서 히어로로 진행되는 과정을 수강해 보세요. 이 과정에서는 머신러닝 또는 TensorFlow.js에 대한 배경 지식이 없다고 가정하고 더 작은 단계에서 알아야 할 모든 내용을 학습합니다.

과정 내용

  • TensorFlow.js의 정의와 다음 웹 앱에서 TensorFlow.js를 사용해야 하는 이유
  • Teachable Machine 사용자 환경을 복제하는 간단한 HTML/CSS /JS 웹페이지를 빌드하는 방법
  • TensorFlow.js를 사용하여 선행 학습된 기본 모델, 특히 MobileNet을 로드하여 전이 학습에 사용할 수 있는 이미지 특성을 생성하는 방법
  • 인식하려는 여러 데이터 클래스에 대해 사용자 웹캠에서 데이터를 수집하는 방법
  • 이미지 특성을 가져와 이를 사용하여 새 객체를 분류하는 방법을 학습하는 다중 레이어 퍼셉트론을 만들고 정의하는 방법입니다.

해킹 시작하기

필요한 항목

  • Glitch.com 계정을 따르는 것이 좋습니다. 또는 직접 수정하고 실행할 수 있는 웹 제공 환경을 사용해도 됩니다.

2 TensorFlow.js란 무엇인가요?

54e81d02971f53e8.png

TensorFlow.js는 자바스크립트로 실행할 수 있는 오픈소스 머신러닝 라이브러리입니다. 또한 Python으로 작성된 원본 TensorFlow 라이브러리를 기반으로 하며, 이 개발자 환경과 자바스크립트 생태계용 API 세트를 다시 만드는 것을 목표로 합니다.

어디에서 사용할 수 있나요?

자바스크립트의 이동성을 감안하여 이제 단일 언어로 작성하고 다음과 같은 모든 플랫폼에서 손쉽게 머신러닝을 수행할 수 있습니다.

  • 바닐라 자바스크립트를 사용하는 웹브라우저의 클라이언트 측
  • 서버 측은 물론 Raspberry Pi와 같은 IoT 기기까지 Node.js 사용
  • Electron을 사용하는 데스크톱 앱
  • React Native를 사용하는 네이티브 모바일 앱

또한 TensorFlow.js는 각 환경 내에서 여러 백엔드 (예: CPU 또는 WebGL과 같이 환경 내에서 실행할 수 있는 실제 하드웨어 기반 환경)를 지원합니다. 이 컨텍스트에서 '백엔드'는 서버 측 환경을 의미하는 것이 아닙니다. 실행을 위한 백엔드가 WebGL에서 클라이언트 측이 될 수 있으므로 호환성을 보장하고 빠른 실행 속도를 유지할 수 있습니다. 현재 TensorFlow.js는 다음을 지원합니다.

  • 기기의 그래픽 카드 (GPU)에서 WebGL 실행 - GPU 가속으로 대형 모델 (크기 3MB 이상)을 실행하는 가장 빠른 방법입니다.
  • CPU에서 웹 어셈블리 (WASM) 실행: 예를 들어 이전 세대 휴대전화를 비롯하여 기기 전반에서 CPU 성능을 개선합니다. 그래픽 프로세서에 콘텐츠를 업로드하는 오버헤드로 인해 WebGL을 사용할 때보다 CPU에서 더 빠르게 실행될 수 있는 소형 모델 (크기가 3MB 미만)에 더 적합합니다.
  • CPU 실행 - 대체를 사용할 수 있는 다른 환경이 없어야 합니다. 세 가지 속도 중 가장 느립니다. 하지만 항상 필요한 정보를 제공해 드립니다.

참고: 실행할 기기를 알고 있는 경우 이 백엔드 중 하나를 강제 적용할 수 있습니다. 아니면 이 백엔드를 자동으로 결정하도록 TensorFlow.js에서 결정해도 됩니다. 지정하지 않습니다.

클라이언트 측 초능력

클라이언트 컴퓨터의 웹브라우저에서 TensorFlow.js를 실행하면 고려해 볼 만한 여러 이점이 있습니다.

개인정보 보호

타사 웹 서버로 데이터를 전송하지 않고도 클라이언트 컴퓨터에서 데이터를 학습시키고 분류할 수 있습니다. 이러한 사례는 GDPR과 같은 현지 법규를 준수해야 하는 경우나 사용자가 자신의 컴퓨터에 보관하고 제3자에게 전송하지 않으려는 데이터를 처리해야 하는 경우가 있습니다.

속도

원격 서버로 데이터를 전송할 필요가 없기 때문에 추론 (데이터 분류 작업)이 더 빨라질 수 있습니다. 또한 사용자가 카메라에 액세스할 수 있게 하면 카메라, 마이크, GPS, 가속도계와 같은 기기의 센서에 직접 액세스할 수도 있습니다.

도달범위 및 규모

클릭 한 번으로 전 세계의 모든 사용자가 내가 보낸 링크를 클릭하여 브라우저에서 웹페이지를 열고 내가 만든 링크를 활용할 수 있습니다. CUDA 드라이버가 포함된 복잡한 서버 측 Linux 설정이 필요 없으며 머신러닝 시스템만 있으면 됩니다.

비용

서버가 없다는 것은 HTML, CSS, JS 및 모델 파일을 호스팅하기 위해 CDN만 있으면 된다는 의미입니다. CDN 비용은 연중무휴로 실행되는 그래픽 카드를 잠재적으로 유지하는 것보다 훨씬 저렴합니다.

서버 측 기능

TensorFlow.js의 Node.js 구현을 활용하면 다음과 같은 기능이 사용 설정됩니다.

완벽한 CUDA 지원

서버 측에서 그래픽 카드 가속의 경우 NVIDIA CUDA 드라이버를 설치하여 TensorFlow가 그래픽 카드와 호환되도록 해야 합니다 (WebGL을 사용하는 브라우저와는 달리 설치 필요 없음). 하지만 CUDA를 완벽하게 지원하므로 그래픽 카드의 하위 수준 기능을 충분히 활용하여 학습과 추론 시간을 단축할 수 있습니다. 성능은 동일한 C++ 백엔드를 공유하므로 Python TensorFlow 구현과 동일합니다.

모델 크기

연구에서 최신 모델의 경우 크기가 매우 큰 모델(기가바이트 포함)을 사용할 수도 있습니다. 브라우저 탭당 메모리 사용량 제한으로 인해 현재 웹브라우저에서 이러한 모델을 실행할 수 없습니다. 이렇게 큰 모델을 실행하려면 모델을 효율적으로 실행하는 데 필요한 하드웨어 사양으로 자체 서버에서 Node.js를 사용하면 됩니다.

IOT

Node.js는 Raspberry Pi와 같이 널리 사용되는 단일 보드 컴퓨터에서 지원됩니다. 따라서 이러한 기기에서도 TensorFlow.js 모델을 실행할 수 있습니다.

속도

Node.js는 자바스크립트로 작성되므로, 적시 컴파일만의 이점을 누릴 수 있습니다. 즉, Node.js를 사용하면 런타임 시 최적화되며, 특히 실행 중인 모든 사전 처리에서 성능이 향상될 수 있습니다. 우수사례에서 Hugging Face가 Node.js를 사용하여 자연어 처리 모델을 2배 개선한 방법을 알아보세요.

지금까지 TensorFlow.js를 실행할 수 있는 기본사항과 몇 가지 이점을 배웠으니 이제 유용한 작업을 시작하겠습니다.

3. 전이 학습

전이 학습이란 정확히 무엇인가요?

전이 학습에서는 이미 다른 것과 비슷한 것을 배우는 데 필요한 지식을 습득하는 과정이 포함됩니다.

인간은 항상 이 일을 한다. 한 번도 본 적 없는 새로운 것을 인식하는 데 사용할 수 있는 평생의 뇌가 있습니다. 예를 들어 이 버드나무는 다음과 같습니다.

e28070392cd4afb9.png

사용자의 지역에 따라 이런 종류의 나무는 본 적이 없을 수도 있습니다.

그러나 아래 새 이미지에 버드나무가 있는지 물어보면 보이는 것과는 다른 각도에서 볼 수 있고 원래 결과와 약간 다르게 보일 수도 있습니다.

d9073a0d5df27222.png

이미 뇌에 많은 수의 뉴런이 있으며, 이 막대는 나무 같은 물체를 식별하는 방법을 알고 있으며 긴 직선을 잘 찾는 다른 뉴런도 있습니다. 이 지식을 재사용하여 버드나무를 빠르게 분류할 수 있습니다. 버드나무는 긴 수직 브랜치가 많은 나무 같은 객체입니다.

마찬가지로 이미지 인식과 같이 도메인에서 이미 학습된 머신러닝 모델이 있는 경우 해당 모델을 재사용하여 관련성이 있지만 다른 작업을 수행할 수 있습니다.

1, 000개의 서로 다른 객체 유형에서 이미지 인식을 수행할 수 있는 매우 인기 있는 연구 모델인 MobileNet과 같은 고급 모델을 사용할 수도 있습니다. 개부터 자동차까지 ImageNet이라는 거대한 데이터 세트에서 학습했으며 라벨이 지정된 수백만 개의 이미지가 포함되어 있습니다.

이 애니메이션에서 MobileNet V1 모델에 있는 매우 많은 수의 레이어를 볼 수 있습니다.

7d4e1e35c1a89715.gif

학습 과정에서 이 모델은 1, 000개의 객체 모두에 중요한 공통 특성을 추출하는 방법을 학습했으며, 이러한 객체를 식별하는 데 사용하는 여러 하위 수준 기능도 이전에 본 적 없는 새로운 객체를 감지하는 데 유용할 수 있습니다. 결국 모든 것은 궁극적으로 선, 질감, 도형의 조합입니다.

MobileNet과 유사한 기존의 컨볼루셔널 신경망(CNN) 아키텍처를 살펴보고, 전이 학습이 이렇게 학습된 네트워크를 활용하여 새로운 것을 배우는 방법을 살펴보겠습니다. 아래 이미지는 0에서 9 사이의 필기 숫자를 인식하도록 학습된 CNN의 일반적인 모델 아키텍처를 보여줍니다.

baf4e3d434576106.png

위에 표시된 것과 같이 기존의 학습된 모델의 선행 학습된 하위 레이어와 오른쪽에 표시된 모델의 끝 근처에 있는 분류 레이어 (모델의 분류 헤드라고도 함)를 서로 분리할 수 있는 경우 하위 수준 레이어를 사용하여 학습시킨 원본 데이터를 기준으로 특정 이미지의 출력 특성을 생성할 수 있습니다. 다음은 분류 헤드가 삭제된 동일한 네트워크입니다.

369a8a9041c6917d.png

인식하려는 새로운 요소가 이전 모델에서 학습한 출력 기능을 활용할 수 있다는 가정하에 새로운 용도로 재사용될 가능성이 높습니다.

위 다이어그램에서 이 가상 모델은 숫자에 따라 학습되었으므로 숫자에 대해 학습한 내용을 a, b, c 등의 문자에도 적용할 수 있습니다.

이제 다음과 같이 a, b, c를 예측하는 새 분류 헤드를 대신 추가할 수 있습니다.

db97e5e60ae73bbd.png

여기서 하위 수준 레이어는 고정되며 학습되지 않습니다. 새로운 분류 헤드만 자동으로 업데이트되어 왼쪽에서 선행 학습된 모델에서 제공하는 특성에 대해 학습합니다.

이 작업을 전이 학습이라고 하며, Teachable Machine이 백그라운드에서 수행합니다.

또한 네트워크 맨 끝에서 다층 퍼셉트론만 학습시키면 전체 네트워크를 처음부터 학습시킬 때보다 훨씬 더 빠르게 학습할 수 있습니다.

하지만 모델의 하위 부분을 손에 넣으려면 어떻게 해야 할까요? 다음 섹션으로 이동하여 알아보세요.

4. TensorFlow Hub - 기본 모델

사용하기에 적합한 기본 모델 찾기

MobileNet과 같은 고급 및 인기 연구 모델의 경우 TensorFlow Hub로 이동한 다음 MobileNet v3 아키텍처를 사용하는 TensorFlow.js에 적합한 모델을 필터링하여 결과를 확인할 수 있습니다. 예를 들면 다음과 같습니다.

C5DC1420C6238C14.PNG

결과 중 일부는 '이미지 분류' 유형(각 모델 카드 결과의 왼쪽 상단에 설명되어 있음)이고 '이미지 특성 벡터' 유형입니다.

이러한 이미지 특징 벡터 결과는 기본적으로 최종 분류 대신 이미지 특성 벡터를 가져오는 데 사용할 수 있는 MobileNet의 미리 준비된 버전입니다.

이와 같은 모델을 '기본 모델'이라고 하며, 이는 이전 분류에서 설명한 것과 동일한 방식으로 새 분류 헤드를 추가하고 자체 데이터로 학습시켜 전이 학습을 수행하는 데 사용할 수 있습니다.

다음으로 확인할 기본 모델은 모델이 출시된 TensorFlow.js 형식입니다. 이러한 특징 벡터 MobileNet v3 모델 중 하나의 페이지를 열면 JS 문서에서 tf.loadGraphModel()를 사용하는 문서의 예시 코드 스니펫을 기반으로 하는 그래프 모델 형식입니다.

f97d903d2e46924b.png

또한 그래프 형식 대신 레이어 형식으로 모델을 찾는 경우 고정할 레이어와 학습을 위해 고정 해제할 레이어를 선택할 수 있습니다. 이는 새 작업의 모델을 만들 때 매우 강력할 수 있으며, '전송 모델'이라고도 합니다. 하지만 지금은 이 튜토리얼에 기본 그래프 모델 유형을 사용하며, 대부분의 TF Hub 모델이 이 형식으로 배포됩니다. 레이어 모델 작업에 대한 자세한 내용은 0에서 히어로로의 TensorFlow.js 과정을 확인하세요.

전이 학습의 이점

전체 모델 아키텍처를 처음부터 학습시키는 대신 전이 학습을 사용할 때의 이점은 무엇인가요?

첫째, 이미 빌드할 기본 모델이 있으므로 전이 학습 접근 방식을 사용하면 학습 시간이 매우 중요합니다.

둘째, 이미 진행한 학습으로 인해 분류하려는 새 항목의 예시를 훨씬 적게 표시할 수 있습니다.

이 방식은 분류하려는 항목의 예시 데이터를 수집할 시간과 리소스가 부족하고, 더 많은 학습 데이터를 수집하기 전에 프로토타입을 신속하게 제작해야 하는 경우 유용합니다.

데이터 수요가 적고 소규모 네트워크를 학습시키는 속도가 느리므로 전이 학습은 리소스 집약성이 덜합니다. 따라서 최신 모델이므로 전체 모델 학습에 몇 시간, 며칠 또는 몇 주 대신 몇 초밖에 걸리지 않으므로 브라우저 환경에 매우 적합합니다.

좋습니다. 이제 전이 학습의 핵심을 이해했으니 이제 Teachable Machine의 자체 버전을 만들 차례입니다. 지금 시작해 보세요.

5 코드 설정

필요한 항목

  • 최신 웹브라우저
  • HTML, CSS, 자바스크립트, Chrome DevTools (콘솔 출력 보기)에 관한 기본 지식

코딩 시작하기

다음으로 시작할 상용구 템플릿 생성 기준:Glitch.com 또는코드펜.io 가 있는지 진단합니다. 클릭 한 번으로 이 템플릿을 이 Codelab의 기본 상태로 클론할 수 있습니다.

Glitch에서 "Remix(리믹스)" 버튼을 클릭하여 포크하고 수정 가능한 새 파일 집합을 만듭니다.

또는 코드펜에서 화면 오른쪽 하단의 '포크'를 클릭합니다.

매우 간단한 골격은 다음 파일을 제공합니다.

  • HTML 페이지 (index.html)
  • 스타일시트 (style.css)
  • 자바스크립트 코드 (script.js)를 작성할 파일

편의를 위해 TensorFlow.js 라이브러리의 HTML 파일에 가져오기가 추가되었습니다. 아키텍처의 형태는 다음과 같습니다.

index.html

<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>

대안: 원하는 웹 편집기를 사용하거나 로컬에서 작업

코드를 다운로드하여 로컬로 또는 다른 온라인 편집기에서 작업하려면 동일한 디렉터리에 위 파일 3개를 만들고 Glitch 상용구에서 이 코드를 각각 복사하여 붙여넣습니다.

6. 앱 HTML 상용구

어디서부터 시작해야 하나요?

모든 프로토타입에는 발견 항목을 렌더링할 수 있는 몇 가지 기본 HTML 스캐폴딩이 필요합니다. 지금 설정하세요. 다음을 추가합니다.

  • 페이지 제목입니다.
  • 일부 설명 텍스트.
  • 상태 단락
  • 준비되면 웹캠 피드를 보유할 동영상
  • 카메라를 시작하거나 데이터를 수집하거나 환경을 재설정하는 버튼 여러 개
  • TensorFlow.js 및 JS 파일의 가져오기는 나중에 코딩할 것입니다.

index.html를 열고 기존 코드를 붙여넣기하여 다음 기능을 설정합니다.

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Transfer Learning - TensorFlow.js</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Import the webpage's stylesheet -->
    <link rel="stylesheet" href="/style.css">
  </head>
  <body>
    <h1>Make your own "Teachable Machine" using Transfer Learning with MobileNet v3 in TensorFlow.js using saved graph model from TFHub.</h1>

    <p id="status">Awaiting TF.js load</p>

    <video id="webcam" autoplay muted></video>

    <button id="enableCam">Enable Webcam</button>
    <button class="dataCollector" data-1hot="0" data-name="Class 1">Gather Class 1 Data</button>
    <button class="dataCollector" data-1hot="1" data-name="Class 2">Gather Class 2 Data</button>
    <button id="train">Train &amp; Predict!</button>
    <button id="reset">Reset</button>

    <!-- Import TensorFlow.js library -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js" type="text/javascript"></script>

    <!-- Import the page's JavaScript to do some stuff -->
    <script type="module" src="/script.js"></script>
  </body>
</html>

세분화

위의 HTML 코드 중 일부를 추가하여 개발자가 추가한 주요 내용을 강조하겠습니다.

  • 페이지 제목의 <h1> 태그와 ID가 'status'인 <p> 태그를 추가했습니다. 이제 시스템의 다른 부분을 사용하여 출력을 확인하므로 정보를 출력합니다.
  • ID가 'webcam'인 <video> 요소를 추가했습니다. 이 요소는 나중에 웹캠 스트림을 렌더링할 것입니다.
  • 5개의 <button> 요소를 추가했습니다. ID가 'enableCam'인 첫 번째 카메라는 카메라를 사용 설정합니다. 다음 두 버튼에는 'dataCollector' 클래스가 있어서 인식하려는 객체의 예시 이미지를 수집할 수 있습니다. 나중에 작성하는 코드는 여러 개의 버튼을 추가할 수 있도록 설계되었으며, 버튼이 자동으로 작동하도록 됩니다.

이러한 버튼에는 data-1hot이라는 특수 사용자 정의 속성도 있습니다. 이 속성에는 첫 번째 클래스의 정수 값이 0부터 시작됩니다. 특정 클래스의 데이터를 나타내는 숫자 색인입니다. ML 모델은 숫자만 사용할 수 있으므로 색인은 문자열 대신 숫자 표현으로 출력 클래스를 올바르게 인코딩하는 데 사용됩니다.

이 클래스에 사용하려는 사람이 읽을 수 있는 이름을 포함하는 데이터 이름 속성도 있습니다. 이를 통해 1개의 핫 인코딩에서 숫자 색인 값을 사용하는 대신 사용자에게 좀 더 의미 있는 이름을 제공할 수 있습니다.

마지막으로 데이터를 수집한 후 학습 프로세스를 시작하거나 각각 앱을 재설정하기 위한 학습 및 재설정 버튼이 있습니다.

  • 또한 <script> 가져오기 2개도 추가했습니다. 하나는 TensorFlow.js용이고 다른 하나는 곧 정의될 script.js용입니다.

7 스타일 추가

요소 기본값

방금 추가한 HTML 요소의 스타일을 추가하여 올바르게 렌더링되도록 하세요. 다음은 위치 및 크기 요소에 올바르게 추가되는 몇 가지 스타일입니다. 특별한 것은 없습니다. Teachable Machine 동영상에서 확인한 것처럼 나중에 추가하여 UX를 더욱 개선할 수 있습니다.

style.css

body {
  font-family: helvetica, arial, sans-serif;
  margin: 2em;
}

h1 {
  font-style: italic;
  color: #FF6F00;
}

video {
  clear: both;
  display: block;
  margin: 10px;
  background: #000000;
  width: 640px;
  height: 480px;
}

button {
  padding: 10px;
  float: left;
  margin: 5px 3px 5px 10px;
}

.removed {
  display: none;
}

#status {
  font-size:150%;
}

완료되었습니다. 출력을 미리 보면 다음과 같이 표시됩니다.

81909685d7566dcb.png

8 자바스크립트: 키 상수 및 리스너

키 상수 정의

먼저 앱 전체에서 사용할 주요 상수를 추가합니다. 먼저 script.js의 내용을 다음 상수로 바꿉니다.

script.js

const STATUS = document.getElementById('status');
const VIDEO = document.getElementById('webcam');
const ENABLE_CAM_BUTTON = document.getElementById('enableCam');
const RESET_BUTTON = document.getElementById('reset');
const TRAIN_BUTTON = document.getElementById('train');
const MOBILE_NET_INPUT_WIDTH = 224;
const MOBILE_NET_INPUT_HEIGHT = 224;
const STOP_DATA_GATHER = -1;
const CLASS_NAMES = [];

분석 대상의 특성은 다음과 같습니다.

  • STATUS는 단순히 상태 업데이트를 작성할 단락 태그에 대한 참조를 보유합니다.
  • VIDEO는 웹캠 피드를 렌더링할 HTML 동영상 요소에 대한 참조를 보유합니다.
  • ENABLE_CAM_BUTTON, RESET_BUTTON, TRAIN_BUTTON는 HTML 페이지에서 모든 키 버튼의 DOM 참조를 가져옵니다.
  • MOBILE_NET_INPUT_WIDTHMOBILE_NET_INPUT_HEIGHT는 각각 MobileNet 모델의 예상 입력 너비와 높이를 정의합니다. 이와 같이 파일 상단 근처에 상수를 저장하여 나중에 다른 버전을 사용하기로 결정하면 여러 위치에서 값을 대체하는 대신 한 번에 값을 더 쉽게 업데이트할 수 있습니다.
  • STOP_DATA_GATHER이 - 1로 설정됩니다. 상태 값을 저장하므로 사용자가 웹캠 피드에서 데이터를 수집하기 위해 버튼을 클릭하는 경우를 알 수 있습니다. 이 숫자에 의미 있는 이름을 지정하면 나중에 코드를 더 쉽게 읽을 수 있습니다.
  • CLASS_NAMES는 조회 역할을 하며 가능한 클래스 예측을 위해 사람이 읽을 수 있는 이름을 보유합니다. 이 배열은 나중에 채워집니다.

이제 주요 요소에 관한 참조가 생겼으므로 몇 가지 이벤트 리스너를 연결할 차례입니다.

주요 이벤트 리스너 추가하기

먼저 다음과 같이 키 버튼에 클릭 이벤트 핸들러를 추가합니다.

script.js

ENABLE_CAM_BUTTON.addEventListener('click', enableCam);
TRAIN_BUTTON.addEventListener('click', trainAndPredict);
RESET_BUTTON.addEventListener('click', reset);

function enableCam() {
  // TODO: Fill this out later in the codelab!
}

function trainAndPredict() {
  // TODO: Fill this out later in the codelab!
}

function reset() {
  // TODO: Fill this out later in the codelab!
}

ENABLE_CAM_BUTTON - 클릭 시 EnableCam 함수를 호출합니다.

TRAIN_BUTTON - 클릭 시 trainAndPredict를 호출합니다.

RESET_BUTTON - 클릭하면 통화가 재설정됩니다.

마지막으로 이 섹션에서는 document.querySelectorAll()를 사용하여 'dataCollector' 클래스가 있는 모든 버튼을 찾을 수 있습니다. 그러면 문서에서 일치하는 요소의 배열이 반환됩니다.

script.js

let dataCollectorButtons = document.querySelectorAll('button.dataCollector');
for (let i = 0; i < dataCollectorButtons.length; i++) {
  dataCollectorButtons[i].addEventListener('mousedown', gatherDataForClass);
  dataCollectorButtons[i].addEventListener('mouseup', gatherDataForClass);
  // Populate the human readable names for classes.
  CLASS_NAMES.push(dataCollectorButtons[i].getAttribute('data-name'));
}

function gatherDataForClass() {
  // TODO: Fill this out later in the codelab!
}

코드 설명:

그런 다음 찾은 버튼을 위아래로 반복하고 두 이벤트 리스너를 각각 연결합니다. 하나는 '마우스다운'용이고 다른 하나는 '마우스업'용입니다. 이렇게 하면 버튼을 누르는 동안 샘플을 계속 녹화할 수 있으므로 데이터 수집에 유용합니다.

두 이벤트 모두 나중에 정의할 gatherDataForClass 함수를 호출합니다.

이 시점에서 검색된 다른 사람이 읽을 수 있는 클래스 이름을 HTML 버튼 속성 data-name에서 CLASS_NAMES 배열로 푸시할 수도 있습니다.

다음으로 나중에 사용할 주요 항목을 저장할 변수를 추가합니다.

script.js

let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;

이제 자세히 살펴보겠습니다.

먼저 로드된 MobileNet 모델을 저장할 mobilenet 변수가 있습니다. 처음에는 이 속성을 정의되지 않음으로 설정합니다.

다음으로 gatherDataState라는 변수가 있습니다. 'dataCollector' 버튼을 누르면 HTML에 정의된 대로 해당 버튼의 핫 ID가 1개로 변경되므로 수집 중인 데이터 클래스를 알 수 있습니다. 처음에는 STOP_DATA_GATHER를 설정합니다. 그러면 버튼을 누르지 않아도 나중에 작성하는 데이터 수집 루프가 데이터를 수집하지 않습니다.

videoPlaying는 웹캠 스트림이 로드 및 재생되고 이를 사용할 수 있는지 추적합니다. ENABLE_CAM_BUTTON. 키를 누를 때까지 웹캠이 켜지지 않으므로 처음에는 false으로 설정됩니다.

다음으로, 배열 두 개(trainingDataInputstrainingDataOutputs)를 정의합니다. MobileNet 기본 모델에서 생성된 입력 특성의 'dataCollector' 버튼을 클릭하고 샘플링된 출력 클래스를 각각 수집하면 수집된 학습 데이터 값이 저장됩니다.

마지막으로 최종 배열인 examplesCount,는 클래스를 추가하기 시작하면 각 클래스에 포함된 예시의 수를 추적하도록 정의됩니다.

마지막으로 예측 루프를 제어하는 predict라는 변수가 있습니다. 초기에는 false로 설정되어 있습니다. 나중에 true으로 설정하기 전에는 예측이 불가능합니다.

이제 모든 주요 변수가 정의되었으므로 이제 분류 대신 이미지 특성 벡터를 제공하는 MobileNet v3 기본 모델을 미리 로드하겠습니다.

9. MobileNet 기본 모델 로드

먼저 아래와 같이 loadMobileNetFeatureModel라는 새 함수를 정의합니다. 모델 로드 작업은 비동기식이므로 비동기 함수여야 합니다.

script.js

/**
 * Loads the MobileNet model and warms it up so ready for use.
 **/
async function loadMobileNetFeatureModel() {
  const URL =
    'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1';

  mobilenet = await tf.loadGraphModel(URL, {fromTFHub: true});
  STATUS.innerText = 'MobileNet v3 loaded successfully!';

  // Warm up the model by passing zeros through it once.
  tf.tidy(function () {
    let answer = mobilenet.predict(tf.zeros([1, MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH, 3]));
    console.log(answer.shape);
  });
}

// Call the function immediately to start loading.
loadMobileNetFeatureModel();

이 코드에서 로드할 모델이 있는 URL를 TFHub 문서에서 정의합니다.

그런 다음 await tf.loadGraphModel()을 사용하여 모델을 로드할 수 있습니다. 이때 Google 웹사이트에서 모델을 로드할 때 특수 속성 fromTFHubtrue로 설정해야 합니다. 이는 이 추가 속성을 설정해야 하는 TF Hub에 호스팅된 모델을 사용하는 경우에만 적용됩니다.

로드가 완료되면 STATUS 요소의 innerText를 메시지로 설정하여 메시지가 올바르게 로드되었으며 데이터 수집을 시작할 준비가 되었는지 확인할 수 있습니다.

이제 모델을 준비해야 합니다. 이 같은 대규모 모델에서는 모델을 처음 사용할 때 모든 것을 설정하는 데 잠시 시간이 걸릴 수 있습니다. 따라서 시간이 더 중요할 수 있는 향후 대기를 방지하기 위해 모델을 통해 0을 전달하는 데 도움이 됩니다.

tf.tidy()에 래핑된 tf.zeros()를 사용하여 배치 크기가 1이고 시작 시 상수에 정의한 올바른 높이와 너비로 텐서를 올바르게 처리할 수 있습니다. 마지막으로 색상 채널을 지정합니다. 이 경우 모델이 RGB 이미지를 예상하므로 3입니다.

다음으로 이 모델이 생성하는 이미지 특성의 크기를 이해하는 데 도움이 되도록 answer.shape()를 사용하여 반환된 텐서의 결과 모양을 로깅합니다.

이 함수를 정의한 후 즉시 함수를 호출하여 페이지 로드 시 모델 다운로드를 시작할 수 있습니다.

지금 실시간 미리보기를 보면 잠시 후 'TF.js 로드 대기 중'에서 'MobileNet v3이 성공적으로 로드됨'으로 상태 텍스트가 변경됩니다. 아래와 같습니다. 계속하기 전에 올바르게 작동하는지 확인하세요.

A28b734e190afff.png

또한 콘솔 출력에서 이 모델이 생성하는 출력 특성의 인쇄 크기를 확인할 수도 있습니다. MobileNet 모델을 통해 0을 실행하면 [1, 1024]의 모양이 출력됩니다. 첫 번째 항목은 배치 크기 1이며, 새 객체를 분류하는 데 도움이 되는 1,024개의 특성을 실제로 반환하는 것을 확인할 수 있습니다.

10. 새 모델 헤드 정의

이제 모델 헤드를 정의할 차례입니다. 모델 헤드는 본질적으로 최소한의 다중 레이어 퍼셉트론입니다.

script.js

let model = tf.sequential();
model.add(tf.layers.dense({inputShape: [1024], units: 128, activation: 'relu'}));
model.add(tf.layers.dense({units: CLASS_NAMES.length, activation: 'softmax'}));

model.summary();

// Compile the model with the defined optimizer and specify a loss function to use.
model.compile({
  // Adam changes the learning rate over time which is useful.
  optimizer: 'adam',
  // Use the correct loss function. If 2 classes of data, must use binaryCrossentropy.
  // Else categoricalCrossentropy is used if more than 2 classes.
  loss: (CLASS_NAMES.length === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy',
  // As this is a classification problem you can record accuracy in the logs too!
  metrics: ['accuracy']
});

이제 이 코드를 살펴보겠습니다. 먼저 모델 레이어를 추가할 tf.순차적 모델을 정의합니다.

다음으로 이 모델에 입력 레이어로 밀집 레이어를 추가합니다. MobileNet v3 기능의 출력이 이 크기이므로 입력 모양은 1024입니다. 모델을 통해 모델을 전달한 후 이전 단계에서 찾았습니다. 이 레이어에는 ReLU 활성화 함수를 사용하는 뉴런 128개가 있습니다.

활성화 함수와 모델 레이어를 처음 사용하는 경우 이 워크숍을 자세히 설명하는 과정을 진행하여 이러한 속성이 내부적으로 어떻게 작동하는지 파악하세요.

다음 레이어는 출력 레이어입니다. 뉴런의 수는 예측하려는 클래스의 수와 같아야 합니다. 이렇게 하려면 CLASS_NAMES.length를 사용하여 분류할 클래스 수를 확인하면 됩니다. 이는 사용자 인터페이스에 있는 데이터 수집 버튼의 수와 같습니다. 이는 분류 문제이므로 회귀 대신 분류 문제를 해결하기 위해 모델을 만들려고 할 때 사용해야 하는 이 출력 레이어의 softmax 활성화를 사용합니다.

이제 model.summary()를 출력하여 새로 정의된 모델의 개요를 콘솔에 출력합니다.

마지막으로 모델을 학습시킬 수 있도록 컴파일합니다. 여기서 옵티마이저는 adam로 설정되고 손실은 CLASS_NAMES.length2이면 binaryCrossentropy가 되고 3개 이상의 클래스가 있는 경우 categoricalCrossentropy를 사용합니다. 분류 정확성 측정항목을 요청하여 나중에 디버깅 목적으로 로그에서 모니터링할 수 있습니다.

콘솔에 다음과 같은 결과가 표시됩니다.

22eaf32286fea4bb.png

여기에는 학습 가능한 매개변수가 13만 개 이상 있습니다. 하지만 이 레이어는 일반 뉴런의 단순 밀도 레이어이므로 학습 속도가 빨라집니다.

프로젝트가 완료된 후 해야 할 작업으로, 첫 번째 레이어의 뉴런 수를 바꿔서 성능을 떨어뜨리면서 얼마나 낮게 설정할 수 있는지 확인해 보세요. 머신러닝에서는 리소스 사용량과 속도 사이에서 최적의 균형을 맞출 수 있는 최적의 매개변수 값을 찾는 데 일정 수준의 시행착오가 발생합니다.

1일 웹캠 사용 설정

이제 앞서 정의한 enableCam() 함수를 만들 차례입니다. 아래와 같이 hasGetUserMedia()라는 새 함수를 추가한 후 이전에 정의된 enableCam() 함수의 콘텐츠를 해당하는 코드로 바꿉니다.

script.js

function hasGetUserMedia() {
  return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}

function enableCam() {
  if (hasGetUserMedia()) {
    // getUsermedia parameters.
    const constraints = {
      video: true,
      width: 640,
      height: 480
    };

    // Activate the webcam stream.
    navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
      VIDEO.srcObject = stream;
      VIDEO.addEventListener('loadeddata', function() {
        videoPlaying = true;
        ENABLE_CAM_BUTTON.classList.add('removed');
      });
    });
  } else {
    console.warn('getUserMedia() is not supported by your browser');
  }
}

먼저 주요 브라우저 API 속성의 존재를 확인하여 브라우저가 getUserMedia()를 지원하는지 확인하는 hasGetUserMedia()라는 함수를 만듭니다.

enableCam() 함수에서 방금 정의한 hasGetUserMedia() 함수를 사용하여 지원되는지 확인합니다. 그렇지 않은 경우 경고를 콘솔에 출력합니다.

지원되는 경우 getUserMedia() 호출에 대해 일부 제약 조건(예: 동영상 스트림만 제한하고, 동영상의 width640픽셀이 됨)을 정의하고 height480픽셀이 되어야 합니다. 사용해야 하는 이유 이보다 큰 동영상은 MobileNet 모델에 공급하려면 224x224픽셀로 크기를 조정해야 하기 때문에 그다지 큰 차이가 없습니다. 더 작은 해상도를 요청하여 일부 컴퓨팅 리소스를 절약할 수도 있습니다. 대부분의 카메라는 이 크기의 해상도를 지원합니다.

그런 다음 위에서 설명한 constraintsnavigator.mediaDevices.getUserMedia()를 호출한 다음 stream가 반환될 때까지 기다립니다. stream가 반환되면 VIDEO 요소를 srcObject 값으로 설정하여 stream를 재생하도록 할 수 있습니다.

또한 VIDEO 요소에 eventListener를 추가하여 stream가 로드되어 성공적으로 재생되는지 알아야 합니다.

스팀이 로드되면 videoPlaying를 true로 설정하고 ENABLE_CAM_BUTTON를 삭제하여 클래스를 'removed'로 설정하여 다시 클릭되지 않도록 할 수 있습니다.

이제 코드를 실행하고 카메라 사용 설정 버튼을 클릭하여 웹캠 액세스를 허용합니다. 처음 만드는 경우 다음과 같이 페이지의 동영상 요소에 렌더링된 것을 확인할 수 있습니다.

b378eb1affa9b883.png

이제 dataCollector 버튼 클릭을 처리할 함수를 추가합니다.

12. 데이터 수집 버튼 이벤트 핸들러

이제 gatherDataForClass().라는 현재 비어 있는 함수를 작성할 차례입니다. 다음은 Codelab을 시작할 때 dataCollector 버튼에 대해 이벤트 핸들러 함수로 할당한 함수입니다.

script.js

/**
 * Handle Data Gather for button mouseup/mousedown.
 **/
function gatherDataForClass() {
  let classNumber = parseInt(this.getAttribute('data-1hot'));
  gatherDataState = (gatherDataState === STOP_DATA_GATHER) ? classNumber : STOP_DATA_GATHER;
  dataGatherLoop();
}

먼저 속성 이름으로 이 속성 this.getAttribute()(이 경우 data-1hot)를 호출하여 현재 클릭된 버튼의 data-1hot 속성을 확인합니다. 문자열이므로 parseInt()을 사용하여 정수로 변환하고 이 결과를 classNumber.이라는 변수에 할당할 수 있습니다.

다음으로 gatherDataState 변수를 적절하게 설정합니다. 현재 gatherDataStateSTOP_DATA_GATHER과 같으면(-1로 설정됨) 현재 데이터가 수집되지 않고 있으며 mousedown 이벤트가 발생한 것입니다. gatherDataState를 방금 찾은 classNumber로 설정합니다.

그 외의 경우에는 현재 데이터를 수집하고 있고 실행된 이벤트가 mouseup 이벤트이며, 이제 해당 클래스의 데이터 수집을 중단하려고 합니다. STOP_DATA_GATHER 상태로 다시 설정하여 곧 정의하게 될 데이터 수집 루프를 종료하면 됩니다.

마지막으로 실제로 클래스 데이터 기록을 실행하는 dataGatherLoop(), 호출을 시작합니다.

13. 데이터 수집

이제 dataGatherLoop() 함수를 정의합니다. 이 함수는 웹캠 동영상에서 이미지를 샘플링하고, MobileNet 모델을 통해 이미지를 전달하고, 해당 모델의 출력 (1024 특성 벡터)을 캡처합니다.

그런 다음 현재 눌러진 버튼의 gatherDataState ID와 함께 이 데이터가 저장되므로 이 데이터가 어떤 클래스를 나타내는지 알 수 있습니다.

이제 자세히 살펴보겠습니다.

script.js

function dataGatherLoop() {
  if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
    let imageFeatures = tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT,
          MOBILE_NET_INPUT_WIDTH], true);
      let normalizedTensorFrame = resizedTensorFrame.div(255);
      return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
    });

    trainingDataInputs.push(imageFeatures);
    trainingDataOutputs.push(gatherDataState);

    // Intialize array index element if currently undefined.
    if (examplesCount[gatherDataState] === undefined) {
      examplesCount[gatherDataState] = 0;
    }
    examplesCount[gatherDataState]++;

    STATUS.innerText = '';
    for (let n = 0; n < CLASS_NAMES.length; n++) {
      STATUS.innerText += CLASS_NAMES[n] + ' data count: ' + examplesCount[n] + '. ';
    }
    window.requestAnimationFrame(dataGatherLoop);
  }
}

videoPlaying가 true인 경우에만 이 함수가 계속 실행됩니다. 즉, 웹캠이 활성 상태이고 gatherDataStateSTOP_DATA_GATHER과 같지 않으며 현재 클래스 데이터 수집 버튼을 누르는 경우입니다.

그런 다음 코드를 tf.tidy()에 래핑하여 다음 코드에서 생성된 텐서를 삭제합니다. 이 tf.tidy() 코드 실행 결과는 imageFeatures이라는 변수에 저장됩니다.

이제 tf.browser.fromPixels()를 사용하여 웹캠 VIDEO의 프레임을 가져올 수 있습니다. 이미지 데이터가 포함된 결과 텐서는 videoFrameAsTensor라는 변수에 저장됩니다.

그런 다음 MobileNet 모델 입력의 올바른 모양이 되도록 videoFrameAsTensor 변수의 크기를 조정합니다. 첫 번째 매개변수로 재구성할 텐서와 함께 tf.image.resizeBilinear() 호출을 사용한 다음 이전에 만든 상수로 정의된 새 높이 및 너비를 정의하는 모양을 사용합니다. 마지막으로, 세 번째 매개변수를 전달하여 크기를 조절할 때 정렬 문제를 피할 수 있도록 모서리를 true로 설정합니다. 크기 조절 결과는 resizedTensorFrame이라는 변수에 저장됩니다.

웹캠 이미지의 크기가 640 x 480픽셀이고 모델에는 224 x 224픽셀의 정사각형 이미지가 필요하므로 이 같은 기본 크기가 조절됩니다.

이 데모의 목적상 괜찮습니다. 그러나 이 Codelab을 완료한 후에는 나중에 만들 수 있는 프로덕션 시스템에서 더 나은 결과를 얻으려면 이 이미지에서 정사각형을 자르는 것이 좋습니다.

다음으로 이미지 데이터를 정규화합니다. 이미지 데이터는 항상 tf.browser.frompixels()을 사용할 때 0~255 범위 내에 있으므로, 모든 값이 0과 1 사이가 되도록 MobileTensor 모델에서는 예상되는 입력 크기인 255를 분할하기만 하면 됩니다.

마지막으로 코드의 tf.tidy() 섹션에서 mobilenet.predict()을 호출하여 로드된 모델을 통해 이 정규화 텐서를 푸시합니다. 여기서 expandDims()를 사용하여 확장된 버전의 normalizedTensorFrame를 전달합니다. 모델이 처리할 입력 배치를 예상하므로 배치 1입니다.

결과가 나오면 즉시 전화를 걸 수 있습니다.squeeze() 반환되는 결과를 반환하여 1D 텐서로 다시 돌아간 다음imageFeatures 결과를 캡처하는 변수tf.tidy() 가 있는지 진단합니다.

이제 MobileNet 모델의 imageFeatures를 가져왔으므로 이를 이전에 정의한 trainingDataInputs 배열에 푸시하여 기록할 수 있습니다.

현재 gatherDataStatetrainingDataOutputs 배열로 푸시하여 이 입력이 무엇을 나타내는지 기록할 수도 있습니다.

gatherDataState 변수는 이전에 정의된 gatherDataForClass() 함수에서 버튼을 클릭할 때 데이터를 기록하는 현재 클래스의 숫자 ID로 설정되었을 것입니다.

이제 특정 클래스의 예시 예시도 늘릴 수 있습니다. 이렇게 하려면 먼저, examplesCount 배열의 색인이 이전에 초기화되었는지 확인합니다. 정의되지 않은 경우 0으로 설정하여 특정 클래스의 숫자 ID에 관한 카운터를 초기화한 다음 현재 gatherDataStateexamplesCount를 늘릴 수 있습니다.

이제 웹페이지에서 STATUS 요소의 텍스트를 업데이트하여 캡처된 각 클래스의 현재 수를 표시합니다. 이렇게 하려면 CLASS_NAMES 배열을 순환하고 사람이 읽을 수 있는 이름과 examplesCount의 동일한 색인에 데이터 수와 결합된 이름을 출력합니다.

마지막으로, 매개변수로 전달된 dataGatherLoop를 사용하여 window.requestAnimationFrame()를 호출하여 이 함수를 다시 반복적으로 호출합니다. 버튼의 mouseup이 감지될 때까지 동영상의 프레임을 계속 샘플링하고 gatherDataStateSTOP_DATA_GATHER,로 설정하면 데이터 수집 루프가 종료됩니다.

지금 코드를 실행하면 카메라 사용 버튼을 클릭하고 웹캠이 로드될 때까지 기다린 다음 각 데이터 수집 버튼을 길게 클릭하여 각 데이터 클래스의 예시를 수집할 수 있습니다. 여기에서 각각 내 휴대전화 및 손 데이터를 수집합니다.

541051644a45131f.gif

위 화면 캡처와 같이 메모리에 모든 텐서를 저장하면 상태 텍스트가 업데이트됩니다.

14. 학습 및 예측

다음 단계는 전이 학습이 이루어지는 현재 비어 있는 trainAndPredict() 함수의 코드를 구현하는 것입니다. 이제 코드를 살펴보겠습니다.

script.js

async function trainAndPredict() {
  predict = false;
  tf.util.shuffleCombo(trainingDataInputs, trainingDataOutputs);
  let outputsAsTensor = tf.tensor1d(trainingDataOutputs, 'int32');
  let oneHotOutputs = tf.oneHot(outputsAsTensor, CLASS_NAMES.length);
  let inputsAsTensor = tf.stack(trainingDataInputs);

  let results = await model.fit(inputsAsTensor, oneHotOutputs, {shuffle: true, batchSize: 5, epochs: 10,
      callbacks: {onEpochEnd: logProgress} });

  outputsAsTensor.dispose();
  oneHotOutputs.dispose();
  inputsAsTensor.dispose();
  predict = true;
  predictLoop();
}

function logProgress(epoch, logs) {
  console.log('Data for epoch ' + epoch, logs);
}

먼저 predictfalse로 설정하여 현재 예측이 실행되지 않도록 합니다.

다음으로, tf.util.shuffleCombo()를 사용하여 입력 및 출력 배열을 셔플링하여 순서가 학습에 문제를 일으키지 않도록 합니다.

출력 배열 trainingDataOutputs,을 int32 유형의 tensor1d로 변환하여 하나의 핫 인코딩에서 사용할 수 있도록 만듭니다. 이 변수는 outputsAsTensor라는 변수에 저장됩니다.

outputsAsTensor 함수와 함께 tf.oneHot() 함수를 사용하여 인코딩할 최대 클래스 수(CLASS_NAMES.length)를 표시합니다. 핫 인코딩된 출력 1개가 이제 oneHotOutputs라는 새 텐서에 저장됩니다.

현재 trainingDataInputs은 기록된 텐서의 배열입니다. 이러한 항목을 학습에 사용하려면 텐서 배열을 일반 2D 텐서로 변환해야 합니다.

TensorFlow.js 라이브러리 내에 tf.stack()라는 멋진 함수가 있습니다.

텐서 배열을 배열하고 함께 쌓아 더 고차원 텐서를 출력으로 만들 수 있습니다. 이 경우 텐서 2D가 반환됩니다. 이는 텐서된 1개의 입력 배치로, 기록된 특성이 학습에 각각 1, 024개인 길이로 학습에 필요합니다.

다음으로 await model.fit()은 커스텀 모델 헤드를 학습시킵니다. 여기서 inputsAsTensor 변수는 oneHotOutputs와 함께 전달하여 각각 입력 및 대상 출력에 사용할 학습 데이터를 나타냅니다. 세 번째 매개변수의 구성 객체에서shuffle -true ,batchSize /5 ,epochs 그걸로 설정10 그런 다음callback 업데이트onEpochEndlogProgress 함수도 곧 정의합니다

마지막으로 생성된 학습이 모델이 학습되면 폐기할 수 있습니다. 그런 다음 predicttrue로 다시 설정하여 예측이 다시 이루어지고 predictLoop() 함수를 호출하여 실시간 웹캠 이미지를 예측할 수 있습니다.

logProcess() 함수를 정의하여 학습 상태를 로깅할 수도 있습니다. 이 함수는 위의 model.fit()에서 사용되고 학습이 끝날 때마다 콘솔에 결과를 출력합니다.

거의 완료되었습니다. 이제 predictLoop() 함수를 추가하여 예측합니다.

핵심 예측 루프

여기서는 웹캠의 프레임을 샘플링하여 프레임에서 실시간으로 내용을 예측하는 기본 예측 루프를 구현하여 브라우저에서 실시간으로 결과를 생성합니다.

코드를 확인해 보겠습니다.

script.js

function predictLoop() {
  if (predict) {
    tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO).div(255);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor,[MOBILE_NET_INPUT_HEIGHT,
          MOBILE_NET_INPUT_WIDTH], true);

      let imageFeatures = mobilenet.predict(resizedTensorFrame.expandDims());
      let prediction = model.predict(imageFeatures).squeeze();
      let highestIndex = prediction.argMax().arraySync();
      let predictionArray = prediction.arraySync();

      STATUS.innerText = 'Prediction: ' + CLASS_NAMES[highestIndex] + ' with ' + Math.floor(predictionArray[highestIndex] * 100) + '% confidence';
    });

    window.requestAnimationFrame(predictLoop);
  }
}

먼저 모델이 학습한 후 사용할 수 있도록 예측이 이루어지도록 predict가 true인지 확인합니다.

그런 다음 dataGatherLoop() 함수에서와 마찬가지로 현재 이미지의 이미지 기능을 가져올 수 있습니다. 기본적으로 tf.browser.from pixels()를 사용하여 웹캠에서 프레임을 가져와 정규화하고, 크기를 224 x 224픽셀로 조정한 다음 MobileNet 모델을 통해 이 데이터를 전달하여 결과 이미지 기능을 가져옵니다.

그러나 이제 새로 학습된 모델 헤드를 사용하여 학습된 모델의 predict() 함수를 통해 방금 찾은 결과 imageFeatures를 전달하여 실제로 예측을 수행할 수 있습니다. 그런 다음 결과 텐서를 짜내서 다시 1차원으로 만들고 prediction이라는 변수에 할당할 수 있습니다.

prediction를 사용하면 argMax()를 사용하여 가장 가치가 높은 색인을 찾은 다음 결과 텐서를 arraySync()를 사용하여 배열로 변환하여 자바스크립트의 기본 데이터를 얻어 위치의 위치를 확인할 수 있습니다 가장 높은 가치 요소 이 값은 highestIndex라는 변수에 저장됩니다.

동일한 이유로 prediction 텐서에서 arraySync()를 직접 호출하여 실제 예측 신뢰도 점수를 얻을 수도 있습니다.

이제 predictionSTATUS 텍스트를 업데이트하는 데 필요한 모든 것을 갖추었습니다. 클래스의 사람이 읽을 수 있는 문자열을 가져오려면 CLASS_NAMES 배열에서 highestIndex를 찾은 다음 predictionArray에서 신뢰값을 가져오면 됩니다. 백분율로 읽기 쉽게 만들려면 100에 결과를 math.floor()만 곱하면 됩니다.

마지막으로, 준비가 되면 window.requestAnimationFrame()를 사용하여 predictionLoop()를 다시 호출하여 동영상 스트림에서 실시간 분류를 가져올 수 있습니다. 새 데이터로 새 모델을 학습하도록 선택하면 predictfalse로 설정될 때까지 계속됩니다.

드디어 마지막에 나오는 퍼즐입니다. 재설정 버튼 구현.

15. 재설정 버튼 구현

거의 완료되었습니다 퍼즐의 마지막 부분은 재설정 버튼을 구현하여 다시 시작하는 것입니다. 현재 비어 있는 reset() 함수의 코드는 다음과 같습니다. 다음과 같이 업데이트합니다.

script.js

/**
 * Purge data and start over. Note this does not dispose of the loaded
 * MobileNet model and MLP head tensors as you will need to reuse
 * them to train a new model.
 **/
function reset() {
  predict = false;
  examplesCount.length = 0;
  for (let i = 0; i < trainingDataInputs.length; i++) {
    trainingDataInputs[i].dispose();
  }
  trainingDataInputs.length = 0;
  trainingDataOutputs.length = 0;
  STATUS.innerText = 'No data collected';

  console.log('Tensors in memory: ' + tf.memory().numTensors);
}

먼저, predictfalse로 설정하여 실행 중인 예측 루프를 중지합니다. 다음으로, 길이를 0으로 설정하여 examplesCount 배열의 모든 콘텐츠를 삭제합니다. 이렇게 하면 배열의 모든 콘텐츠를 간편하게 삭제할 수 있습니다.

이제 현재 기록된 trainingDataInputs를 모두 살펴보고 메모리에 포함된 각 텐서의 dispose()를 확인하여 메모리를 다시 확보합니다. 자바스크립트가 가비지 컬렉터에 의해 텐서가 정리되지 않기 때문입니다.

이제 trainingDataInputstrainingDataOutputs 배열 모두에서 배열 길이를 0으로 안전하게 설정하여 삭제할 수 있습니다.

마지막으로 STATUS 텍스트를 적절한 정보로 설정하고 상태 검사로 메모리에 남아 있는 텐서를 출력합니다.

MobileNet 모델과 정의된 멀티 레이어 퍼셉트론은 모두 폐기되지 않으므로 수백 개의 텐서가 메모리에 남아 있습니다. 재설정 후 다시 학습하려면 새로운 학습 데이터로 재사용해야 합니다.

16. 한번 연습해 봅시다.

이제 Teachable Machine의 자체 버전을 테스트할 차례입니다.

실시간 미리보기로 이동하여 웹캠을 사용 설정하고 방 안의 일부 개체에서는 클래스 1에 관한 샘플을 30개 이상 수집한 후 다른 개체에서도 클래스 2에 대해 동일한 작업을 하고 학습한 후 콘솔 로그를 확인하여 진행 상황을 확인합니다. 상당히 빠른 학습이 가능합니다.

소라카

학습이 완료되면 카메라에 객체를 표시하여 상단 근처의 웹페이지 상태 텍스트 영역에 출력될 실시간 예측을 가져옵니다. 문제가 있는 경우 완료된 작업 코드를 확인하여 복사하지 못한 내용이 있는지 확인하세요.

17. 축하합니다

축하합니다. 브라우저에서 TensorFlow.js를 사용한 첫 번째 전이 학습 예시를 완료했습니다.

사용해 보고 다양한 객체에서 테스트해보세요. 특히 다른 항목과 비슷한 경우 다른 객체보다 인식하기가 더 어려울 수 있습니다. 구분하려면 수업이나 학습 데이터를 더 추가해야 할 수 있습니다.

요약

이 Codelab에서 배운 내용은 다음과 같습니다.

  1. 전이 학습의 정의와 전체 모델 학습에 비해의 이점이 있습니다.
  2. TensorFlow Hub에서 재사용할 모델을 가져오는 방법
  3. 전이 학습에 적합한 웹 앱을 설정하는 방법
  4. 기본 모델을 로드하고 사용하여 이미지 특성을 생성하는 방법
  5. 웹캠 이미지에서 커스텀 객체를 인식할 수 있는 새 예측 헤드를 학습시키는 방법
  6. 결과 모델을 사용하여 실시간으로 데이터를 분류하는 방법

다음 단계

작업을 시작할 준비가 되었다면 실제로 사용할 수 있는 실제 사용 사례에 맞게 이 머신러닝 모델 상용구를 확장하려면 어떤 광고 소재 아이디어를 떠올릴 수 있을까요? 회사의 직원들이 일상 업무에서 중요한 항목을 분류하도록 모델을 학습시키는 데 현재 종사하는 업계에 혁신을 불러올 수 있을까요? 가능성은 무한합니다.

더 나아가 이 전체 과정을 무료로 수강하는 것이 좋습니다. 현재 이 Codelab에 있는 모델 2개를 효율을 위해 단일 모델로 결합하는 방법을 보여줍니다.

또한 기존의 Teachable Machine 애플리케이션 배경 이론에 관해 궁금하다면 이 튜토리얼을 참고하세요.

결과물 공유

오늘날의 창의적인 작업을 다른 창작 사례에 손쉽게 적용할 수 있습니다. 기존과는 달리 사고를 멈추고 계속 해킹하는 것이 좋습니다.

잊지 말고 소셜 미디어에서#MadeWithTFJS 프로젝트가 Google에서 추천될 수 있는 해시태그TensorFlow 블로그 심지어향후 일정 가 있는지 진단합니다. 여러분의 의견을 기다리겠습니다.

확인할 웹사이트