<commit_before>{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WKIIJrOaR2rL"
      },
      "source": [
        "**Copyright 2021 The TensorFlow Authors.**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "mEE8NFIMSGO-"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/model_optimization/guide/combine/cqat_example\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\" class=\"_active_edit_href\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" class=\"\">GitHub에서 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/model-optimization/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SyiSRgdtSGPC"
      },
      "source": [
        "# 클러스터 보존 양자화 인식 훈련(CQAT) Keras 예제"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dKnJyAaASGPD"
      },
      "source": [
        "## 개요\n",
        "\n",
        "이것은 TensorFlow Model Optimization Toolkit의 협업 최적화 파이프라인의 일부인 **클러스터 보존 양자화 인식 교육(CQAT)** API의 사용을 보여주는 종단 간 예제입니다.\n",
        "\n",
        "### 다른 페이지\n",
        "\n",
        "파이프라인 및 기타 사용 가능한 기술에 대한 소개는 [협업 최적화 개요 페이지 를 참조하십시오](https://www.tensorflow.org/model_optimization/guide/combine/collaborative_optimization) .\n",
        "\n",
        "### 내용물\n",
        "\n",
        "튜토리얼에서는 다음을 수행합니다.\n",
        "\n",
        "1. MNIST 데이터 세트에 대한 `tf.keras` 모델을 처음부터 훈련시킵니다.\n",
        "2. 클러스터링으로 모델을 미세 조정하고 정확도를 확인합니다.\n",
        "3. QAT를 적용하고 클러스터의 손실을 관찰합니다.\n",
        "4. CQAT를 적용하고 이전에 적용된 클러스터링이 보존되었는지 관찰합니다.\n",
        "5. TFLite 모델을 생성하고 CQAT를 적용한 효과를 관찰합니다.\n",
        "6. 훈련 후 양자화를 사용하여 양자화된 모델과 달성된 CQAT 모델 정확도를 비교합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RgcQznnZSGPE"
      },
      "source": [
        "## 설정\n",
        "\n",
        "이 Jupyter Notebook은 로컬 [virtualenv](https://www.tensorflow.org/install/pip?lang=python3#2.-create-a-virtual-environment-recommended) 또는 [colab](https://colab.sandbox.google.com/) 에서 실행할 수 있습니다. 종속성 설정에 대한 자세한 내용은 [설치 가이드](https://www.tensorflow.org/model_optimization/guide/install) 를 참조하십시오. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3asgXMqnSGPE"
      },
      "outputs": [],
      "source": [
        "! pip install -q tensorflow-model-optimization"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gL6JiLXkSGPI"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "import numpy as np\n",
        "import tempfile\n",
        "import zipfile\n",
        "import os"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dKzOfl5FSGPL"
      },
      "source": [
        "## 클러스터링 없이 MNIST용 tf.keras 모델 학습"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w7Fd6jZ7SGPL"
      },
      "outputs": [],
      "source": [
        "# Load MNIST dataset\n",
        "mnist = tf.keras.datasets.mnist\n",
        "(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\n",
        "\n",
        "# Normalize the input image so that each pixel value is between 0 to 1.\n",
        "train_images = train_images / 255.0\n",
        "test_images  = test_images / 255.0\n",
        "\n",
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.InputLayer(input_shape=(28, 28)),\n",
        "  tf.keras.layers.Reshape(target_shape=(28, 28, 1)),\n",
        "  tf.keras.layers.Conv2D(filters=12, kernel_size=(3, 3),\n",
        "                         activation=tf.nn.relu),\n",
        "  tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\n",
        "  tf.keras.layers.Flatten(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])\n",
        "\n",
        "# Train the digit classification model\n",
        "model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "model.fit(\n",
        "    train_images,\n",
        "    train_labels,\n",
        "    validation_split=0.1,\n",
        "    epochs=10\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rBOQ8MeESGPO"
      },
      "source": [
        "### 기준 모델을 평가하고 나중에 사용하기 위해 저장합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "HYulekocSGPP"
      },
      "outputs": [],
      "source": [
        "_, baseline_model_accuracy = model.evaluate(\n",
        "    test_images, test_labels, verbose=0)\n",
        "\n",
        "print('Baseline test accuracy:', baseline_model_accuracy)\n",
        "\n",
        "_, keras_file = tempfile.mkstemp('.h5')\n",
        "print('Saving model to: ', keras_file)\n",
        "tf.keras.models.save_model(model, keras_file, include_optimizer=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cWPgcnjKSGPR"
      },
      "source": [
        "## 8개의 클러스터로 모델을 클러스터링하고 미세 조정"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Y2wKK7w9SGPS"
      },
      "source": [
        "`cluster_weights()` API를 적용하여 사전 훈련된 전체 모델을 클러스터링하여 정확도를 유지하면서 zip을 적용할 때 모델 크기를 줄이는 효과를 보여주고 관찰합니다. API를 사용하여 목표 정확도를 유지하면서 최고의 압축률을 달성하는 방법은 [클러스터링 종합 가이드 를](https://www.tensorflow.org/model_optimization/guide/clustering/clustering_comprehensive_guide) 참조하십시오."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ea40z522SGPT"
      },
      "source": [
        "### 모델 정의 및 클러스터링 API 적용"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7aOB5vjOZMTS"
      },
      "source": [
        "클러스터링 API를 사용하기 전에 모델을 사전 학습해야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OzqKKt0mSGPT"
      },
      "outputs": [],
      "source": [
        "import tensorflow_model_optimization as tfmot\n",
        "\n",
        "cluster_weights = tfmot.clustering.keras.cluster_weights\n",
        "CentroidInitialization = tfmot.clustering.keras.CentroidInitialization\n",
        "\n",
        "clustering_params = {\n",
        "  'number_of_clusters': 8,\n",
        "  'cluster_centroids_init': CentroidInitialization.KMEANS_PLUS_PLUS,\n",
        "  'cluster_per_channel': True,\n",
        "}\n",
        "\n",
        "clustered_model = cluster_weights(model, **clustering_params)\n",
        "\n",
        "# Use smaller learning rate for fine-tuning\n",
        "opt = tf.keras.optimizers.Adam(learning_rate=1e-5)\n",
        "\n",
        "clustered_model.compile(\n",
        "  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "  optimizer=opt,\n",
        "  metrics=['accuracy'])\n",
        "\n",
        "clustered_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ev4MyClmSGPW"
      },
      "source": [
        "### 모델 미세 조정 및 기준선에 대한 정확도 평가"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vQoy9CcASGPX"
      },
      "source": [
        "3 Epoch에 대한 클러스터링으로 모델을 미세 조정합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jn29-coXSGPX"
      },
      "outputs": [],
      "source": [
        "# Fine-tune model\n",
        "clustered_model.fit(\n",
        "  train_images,\n",
        "  train_labels,\n",
        "  epochs=3,\n",
        "  validation_split=0.1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iHCYA7twR2ro"
      },
      "source": [
        "모델의 각 커널에서 클러스터링 수를 계산하고 인쇄하는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f3gf1TDjR2rp"
      },
      "outputs": [],
      "source": [
        "def print_model_weight_clusters(model):\n",
        "\n",
        "    for layer in model.layers:\n",
        "        if isinstance(layer, tf.keras.layers.Wrapper):\n",
        "            weights = layer.trainable_weights\n",
        "        else:\n",
        "            weights = layer.weights\n",
        "        for weight in weights:\n",
        "            # ignore auxiliary quantization weights\n",
        "            if \"quantize_layer\" in weight.name:\n",
        "                continue\n",
        "            if \"kernel\" in weight.name:\n",
        "                unique_count = len(np.unique(weight))\n",
        "                print(\n",
        "                    f\"{layer.name}/{weight.name}: {unique_count} clusters \"\n",
        "                )"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QS3VBSXAR2rq"
      },
      "source": [
        "모델 커널이 올바르게 클러스터링되었는지 확인하십시오. 먼저 클러스터링 래퍼를 제거해야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5l1jOLMfR2rq"
      },
      "outputs": [],
      "source": [
        "stripped_clustered_model = tfmot.clustering.keras.strip_clustering(clustered_model)\n",
        "\n",
        "print_model_weight_clusters(stripped_clustered_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dvaZKoxtTORx"
      },
      "source": [
        "이 예의 경우 기준선과 비교하여 클러스터링 후 테스트 정확도의 손실이 최소화됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bE7MxpWLTaQ1"
      },
      "outputs": [],
      "source": [
        "_, clustered_model_accuracy = clustered_model.evaluate(\n",
        "  test_images, test_labels, verbose=0)\n",
        "\n",
        "print('Baseline test accuracy:', baseline_model_accuracy)\n",
        "print('Clustered test accuracy:', clustered_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VXfPMa6ISGPd"
      },
      "source": [
        "## QAT 및 CQAT를 적용하고 두 경우 모두 모델 클러스터에 미치는 영향 확인"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1zr_QIhcUeuC"
      },
      "source": [
        "다음으로 클러스터링된 모델에 QAT와 클러스터 보존 QAT(CQAT)를 모두 적용하고 CQAT가 클러스터링된 모델에서 가중치 클러스터를 보존하는 것을 관찰합니다. CQAT API를 적용하기 전에 `tfmot.clustering.keras.strip_clustering` 을 사용하여 모델에서 클러스터링 래퍼를 제거했습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4h6tSvMzSGPd"
      },
      "outputs": [],
      "source": [
        "# QAT\n",
        "qat_model = tfmot.quantization.keras.quantize_model(stripped_clustered_model)\n",
        "\n",
        "qat_model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "print('Train qat model:')\n",
        "qat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)\n",
        "\n",
        "# CQAT\n",
        "quant_aware_annotate_model = tfmot.quantization.keras.quantize_annotate_model(\n",
        "              stripped_clustered_model)\n",
        "cqat_model = tfmot.quantization.keras.quantize_apply(\n",
        "              quant_aware_annotate_model,\n",
        "              tfmot.experimental.combine.Default8BitClusterPreserveQuantizeScheme())\n",
        "\n",
        "cqat_model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "print('Train cqat model:')\n",
        "cqat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-25FRoM0R2rt"
      },
      "outputs": [],
      "source": [
        "print(\"QAT Model clusters:\")\n",
        "print_model_weight_clusters(qat_model)\n",
        "print(\"CQAT Model clusters:\")\n",
        "print_model_weight_clusters(cqat_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rdUFrE9CR2ru"
      },
      "source": [
        "## CQAT 모델의 압축 이점 보기\n",
        "\n",
        "압축된 모델 파일을 가져오는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gc5txUkwR2ru"
      },
      "outputs": [],
      "source": [
        "def get_gzipped_model_size(file):\n",
        "  # It returns the size of the gzipped model in kilobytes.\n",
        "\n",
        "  _, zipped_file = tempfile.mkstemp('.zip')\n",
        "  with zipfile.ZipFile(zipped_file, 'w', compression=zipfile.ZIP_DEFLATED) as f:\n",
        "    f.write(file)\n",
        "\n",
        "  return os.path.getsize(zipped_file)/1000"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "405ju8jER2ru"
      },
      "source": [
        "이것은 작은 모델입니다. 클러스터링과 CQAT를 더 큰 프로덕션 모델에 적용하면 압축률이 더 높아집니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OChikLlhR2rv"
      },
      "outputs": [],
      "source": [
        "# QAT model\n",
        "converter = tf.lite.TFLiteConverter.from_keras_model(qat_model)\n",
        "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
        "qat_tflite_model = converter.convert()\n",
        "qat_model_file = 'qat_model.tflite'\n",
        "# Save the model.\n",
        "with open(qat_model_file, 'wb') as f:\n",
        "    f.write(qat_tflite_model)\n",
        "    \n",
        "# CQAT model\n",
        "converter = tf.lite.TFLiteConverter.from_keras_model(cqat_model)\n",
        "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
        "cqat_tflite_model = converter.convert()\n",
        "cqat_model_file = 'cqat_model.tflite'\n",
        "# Save the model.\n",
        "with open(cqat_model_file, 'wb') as f:\n",
        "    f.write(cqat_tflite_model)\n",
        "    \n",
        "print(\"QAT model size: \", get_gzipped_model_size(qat_model_file), ' KB')\n",
        "print(\"CQAT model size: \", get_gzipped_model_size(cqat_model_file), ' KB')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-zwAgK4SR2rw"
      },
      "source": [
        "## TF에서 TFLite까지의 정확도 지속성 확인\n",
        "\n",
        "테스트 데이터 세트에서 TFLite 모델을 평가하는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BEeTH_qBR2rw"
      },
      "outputs": [],
      "source": [
        "def eval_model(interpreter):\n",
        "  input_index = interpreter.get_input_details()[0][\"index\"]\n",
        "  output_index = interpreter.get_output_details()[0][\"index\"]\n",
        "\n",
        "  # Run predictions on every image in the \"test\" dataset.\n",
        "  prediction_digits = []\n",
        "  for i, test_image in enumerate(test_images):\n",
        "    if i % 1000 == 0:\n",
        "      print(f\"Evaluated on {i} results so far.\")\n",
        "    # Pre-processing: add batch dimension and convert to float32 to match with\n",
        "    # the model's input data format.\n",
        "    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)\n",
        "    interpreter.set_tensor(input_index, test_image)\n",
        "\n",
        "    # Run inference.\n",
        "    interpreter.invoke()\n",
        "\n",
        "    # Post-processing: remove batch dimension and find the digit with highest\n",
        "    # probability.\n",
        "    output = interpreter.tensor(output_index)\n",
        "    digit = np.argmax(output()[0])\n",
        "    prediction_digits.append(digit)\n",
        "\n",
        "  print('\\n')\n",
        "  # Compare prediction results with ground truth labels to calculate accuracy.\n",
        "  prediction_digits = np.array(prediction_digits)\n",
        "  accuracy = (prediction_digits == test_labels).mean()\n",
        "  return accuracy"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CiL7XluNR2rx"
      },
      "source": [
        "클러스터링되고 양자화된 모델을 평가한 다음 TensorFlow의 정확도가 TFLite 백엔드에서 지속되는지 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LLHIyrumR2rx"
      },
      "outputs": [],
      "source": [
        "interpreter = tf.lite.Interpreter(cqat_model_file)\n",
        "interpreter.allocate_tensors()\n",
        "\n",
        "cqat_test_accuracy = eval_model(interpreter)\n",
        "\n",
        "print('Clustered and quantized TFLite test_accuracy:', cqat_test_accuracy)\n",
        "print('Clustered TF test accuracy:', clustered_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YokQ0TuvR2ry"
      },
      "source": [
        "## 훈련 후 양자화 적용 및 CQAT 모델과 비교\n",
        "\n",
        "Next, we use post-training quantization (no fine-tuning) on the clustered model and check its accuracy against the CQAT model. This demonstrates why you would need to use CQAT to improve the quantized model's accuracy. The difference may not be very visible, because the MNIST model is quite small and overparametrized.\n",
        "\n",
        "먼저 처음 1000개의 훈련 이미지에서 캘리브레이션 데이터 세트에 대한 생성기를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LoVVjF-zR2ry"
      },
      "outputs": [],
      "source": [
        "def mnist_representative_data_gen():\n",
        "  for image in train_images[:1000]:  \n",
        "    image = np.expand_dims(image, axis=0).astype(np.float32)\n",
        "    yield [image]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KS6MMKVFR2ry"
      },
      "source": [
        "모델을 양자화하고 이전에 획득한 CQAT 모델과 정확도를 비교합니다. 미세 조정으로 양자화된 모델은 더 높은 정확도를 달성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {<commit_msg>Translate cqat_example.ipynb via GitLocalize<commit_after>{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WKIIJrOaR2rL"
      },
      "source": [
        "**Copyright 2021 The TensorFlow Authors.**"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "mEE8NFIMSGO-"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MfBg1C5NB3X0"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/model_optimization/guide/combine/cqat_example\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\" class=\"_active_edit_href\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" class=\"\">GitHub에서 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/model-optimization/tensorflow_model_optimization/g3doc/guide/combine/cqat_example.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "SyiSRgdtSGPC"
      },
      "source": [
        "# 클러스터 보존 양자화 인식 훈련(CQAT) Keras 예제"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dKnJyAaASGPD"
      },
      "source": [
        "## 개요\n",
        "\n",
        "이것은 TensorFlow Model Optimization Toolkit의 협업 최적화 파이프라인의 일부인 **클러스터 보존 양자화 인식 교육(CQAT)** API의 사용을 보여주는 종단 간 예제입니다.\n",
        "\n",
        "### 다른 페이지\n",
        "\n",
        "파이프라인 및 기타 사용 가능한 기술에 대한 소개는 [협업 최적화 개요 페이지 를 참조하십시오](https://www.tensorflow.org/model_optimization/guide/combine/collaborative_optimization) .\n",
        "\n",
        "### 내용물\n",
        "\n",
        "튜토리얼에서는 다음을 수행합니다.\n",
        "\n",
        "1. MNIST 데이터 세트에 대한 `tf.keras` 모델을 처음부터 훈련시킵니다.\n",
        "2. 클러스터링으로 모델을 미세 조정하고 정확도를 확인합니다.\n",
        "3. QAT를 적용하고 클러스터의 손실을 관찰합니다.\n",
        "4. CQAT를 적용하고 이전에 적용된 클러스터링이 보존되었는지 관찰합니다.\n",
        "5. TFLite 모델을 생성하고 CQAT를 적용한 효과를 관찰합니다.\n",
        "6. 훈련 후 양자화를 사용하여 양자화된 모델과 달성된 CQAT 모델 정확도를 비교합니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RgcQznnZSGPE"
      },
      "source": [
        "## 설정\n",
        "\n",
        "이 Jupyter Notebook은 로컬 [virtualenv](https://www.tensorflow.org/install/pip?lang=python3#2.-create-a-virtual-environment-recommended) 또는 [colab](https://colab.sandbox.google.com/) 에서 실행할 수 있습니다. 종속성 설정에 대한 자세한 내용은 [설치 가이드](https://www.tensorflow.org/model_optimization/guide/install) 를 참조하십시오. "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3asgXMqnSGPE"
      },
      "outputs": [],
      "source": [
        "! pip install -q tensorflow-model-optimization"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gL6JiLXkSGPI"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "\n",
        "import numpy as np\n",
        "import tempfile\n",
        "import zipfile\n",
        "import os"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dKzOfl5FSGPL"
      },
      "source": [
        "## 클러스터링 없이 MNIST용 tf.keras 모델 학습"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w7Fd6jZ7SGPL"
      },
      "outputs": [],
      "source": [
        "# Load MNIST dataset\n",
        "mnist = tf.keras.datasets.mnist\n",
        "(train_images, train_labels), (test_images, test_labels) = mnist.load_data()\n",
        "\n",
        "# Normalize the input image so that each pixel value is between 0 to 1.\n",
        "train_images = train_images / 255.0\n",
        "test_images  = test_images / 255.0\n",
        "\n",
        "model = tf.keras.Sequential([\n",
        "  tf.keras.layers.InputLayer(input_shape=(28, 28)),\n",
        "  tf.keras.layers.Reshape(target_shape=(28, 28, 1)),\n",
        "  tf.keras.layers.Conv2D(filters=12, kernel_size=(3, 3),\n",
        "                         activation=tf.nn.relu),\n",
        "  tf.keras.layers.MaxPooling2D(pool_size=(2, 2)),\n",
        "  tf.keras.layers.Flatten(),\n",
        "  tf.keras.layers.Dense(10)\n",
        "])\n",
        "\n",
        "# Train the digit classification model\n",
        "model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "model.fit(\n",
        "    train_images,\n",
        "    train_labels,\n",
        "    validation_split=0.1,\n",
        "    epochs=10\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rBOQ8MeESGPO"
      },
      "source": [
        "### 기준 모델을 평가하고 나중에 사용하기 위해 저장합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "HYulekocSGPP"
      },
      "outputs": [],
      "source": [
        "_, baseline_model_accuracy = model.evaluate(\n",
        "    test_images, test_labels, verbose=0)\n",
        "\n",
        "print('Baseline test accuracy:', baseline_model_accuracy)\n",
        "\n",
        "_, keras_file = tempfile.mkstemp('.h5')\n",
        "print('Saving model to: ', keras_file)\n",
        "tf.keras.models.save_model(model, keras_file, include_optimizer=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cWPgcnjKSGPR"
      },
      "source": [
        "## 8개의 클러스터로 모델을 클러스터링하고 미세 조정"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Y2wKK7w9SGPS"
      },
      "source": [
        "`cluster_weights()` API를 적용하여 사전 훈련된 전체 모델을 클러스터링하여 정확도를 유지하면서 zip을 적용할 때 모델 크기를 줄이는 효과를 보여주고 관찰합니다. API를 사용하여 목표 정확도를 유지하면서 최고의 압축률을 달성하는 방법은 [클러스터링 종합 가이드 를](https://www.tensorflow.org/model_optimization/guide/clustering/clustering_comprehensive_guide) 참조하십시오."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ea40z522SGPT"
      },
      "source": [
        "### 모델 정의 및 클러스터링 API 적용"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7aOB5vjOZMTS"
      },
      "source": [
        "클러스터링 API를 사용하기 전에 모델을 사전 학습해야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OzqKKt0mSGPT"
      },
      "outputs": [],
      "source": [
        "import tensorflow_model_optimization as tfmot\n",
        "\n",
        "cluster_weights = tfmot.clustering.keras.cluster_weights\n",
        "CentroidInitialization = tfmot.clustering.keras.CentroidInitialization\n",
        "\n",
        "clustering_params = {\n",
        "  'number_of_clusters': 8,\n",
        "  'cluster_centroids_init': CentroidInitialization.KMEANS_PLUS_PLUS,\n",
        "  'cluster_per_channel': True,\n",
        "}\n",
        "\n",
        "clustered_model = cluster_weights(model, **clustering_params)\n",
        "\n",
        "# Use smaller learning rate for fine-tuning\n",
        "opt = tf.keras.optimizers.Adam(learning_rate=1e-5)\n",
        "\n",
        "clustered_model.compile(\n",
        "  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "  optimizer=opt,\n",
        "  metrics=['accuracy'])\n",
        "\n",
        "clustered_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ev4MyClmSGPW"
      },
      "source": [
        "### 모델 미세 조정 및 기준선에 대한 정확도 평가"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vQoy9CcASGPX"
      },
      "source": [
        "3 Epoch에 대한 클러스터링으로 모델을 미세 조정합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jn29-coXSGPX"
      },
      "outputs": [],
      "source": [
        "# Fine-tune model\n",
        "clustered_model.fit(\n",
        "  train_images,\n",
        "  train_labels,\n",
        "  epochs=3,\n",
        "  validation_split=0.1)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iHCYA7twR2ro"
      },
      "source": [
        "모델의 각 커널에서 클러스터링 수를 계산하고 인쇄하는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f3gf1TDjR2rp"
      },
      "outputs": [],
      "source": [
        "def print_model_weight_clusters(model):\n",
        "\n",
        "    for layer in model.layers:\n",
        "        if isinstance(layer, tf.keras.layers.Wrapper):\n",
        "            weights = layer.trainable_weights\n",
        "        else:\n",
        "            weights = layer.weights\n",
        "        for weight in weights:\n",
        "            # ignore auxiliary quantization weights\n",
        "            if \"quantize_layer\" in weight.name:\n",
        "                continue\n",
        "            if \"kernel\" in weight.name:\n",
        "                unique_count = len(np.unique(weight))\n",
        "                print(\n",
        "                    f\"{layer.name}/{weight.name}: {unique_count} clusters \"\n",
        "                )"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QS3VBSXAR2rq"
      },
      "source": [
        "모델 커널이 올바르게 클러스터링되었는지 확인하십시오. 먼저 클러스터링 래퍼를 제거해야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5l1jOLMfR2rq"
      },
      "outputs": [],
      "source": [
        "stripped_clustered_model = tfmot.clustering.keras.strip_clustering(clustered_model)\n",
        "\n",
        "print_model_weight_clusters(stripped_clustered_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dvaZKoxtTORx"
      },
      "source": [
        "이 예의 경우 기준선과 비교하여 클러스터링 후 테스트 정확도의 손실이 최소화됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "bE7MxpWLTaQ1"
      },
      "outputs": [],
      "source": [
        "_, clustered_model_accuracy = clustered_model.evaluate(\n",
        "  test_images, test_labels, verbose=0)\n",
        "\n",
        "print('Baseline test accuracy:', baseline_model_accuracy)\n",
        "print('Clustered test accuracy:', clustered_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VXfPMa6ISGPd"
      },
      "source": [
        "## QAT 및 CQAT를 적용하고 두 경우 모두 모델 클러스터에 미치는 영향 확인"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1zr_QIhcUeuC"
      },
      "source": [
        "다음으로 클러스터링된 모델에 QAT와 클러스터 보존 QAT(CQAT)를 모두 적용하고 CQAT가 클러스터링된 모델에서 가중치 클러스터를 보존하는 것을 관찰합니다. CQAT API를 적용하기 전에 `tfmot.clustering.keras.strip_clustering` 을 사용하여 모델에서 클러스터링 래퍼를 제거했습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4h6tSvMzSGPd"
      },
      "outputs": [],
      "source": [
        "# QAT\n",
        "qat_model = tfmot.quantization.keras.quantize_model(stripped_clustered_model)\n",
        "\n",
        "qat_model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "print('Train qat model:')\n",
        "qat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)\n",
        "\n",
        "# CQAT\n",
        "quant_aware_annotate_model = tfmot.quantization.keras.quantize_annotate_model(\n",
        "              stripped_clustered_model)\n",
        "cqat_model = tfmot.quantization.keras.quantize_apply(\n",
        "              quant_aware_annotate_model,\n",
        "              tfmot.experimental.combine.Default8BitClusterPreserveQuantizeScheme())\n",
        "\n",
        "cqat_model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "print('Train cqat model:')\n",
        "cqat_model.fit(train_images, train_labels, batch_size=128, epochs=1, validation_split=0.1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-25FRoM0R2rt"
      },
      "outputs": [],
      "source": [
        "print(\"QAT Model clusters:\")\n",
        "print_model_weight_clusters(qat_model)\n",
        "print(\"CQAT Model clusters:\")\n",
        "print_model_weight_clusters(cqat_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rdUFrE9CR2ru"
      },
      "source": [
        "## CQAT 모델의 압축 이점 보기\n",
        "\n",
        "압축된 모델 파일을 가져오는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gc5txUkwR2ru"
      },
      "outputs": [],
      "source": [
        "def get_gzipped_model_size(file):\n",
        "  # It returns the size of the gzipped model in kilobytes.\n",
        "\n",
        "  _, zipped_file = tempfile.mkstemp('.zip')\n",
        "  with zipfile.ZipFile(zipped_file, 'w', compression=zipfile.ZIP_DEFLATED) as f:\n",
        "    f.write(file)\n",
        "\n",
        "  return os.path.getsize(zipped_file)/1000"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "405ju8jER2ru"
      },
      "source": [
        "이것은 작은 모델입니다. 클러스터링과 CQAT를 더 큰 프로덕션 모델에 적용하면 압축률이 더 높아집니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OChikLlhR2rv"
      },
      "outputs": [],
      "source": [
        "# QAT model\n",
        "converter = tf.lite.TFLiteConverter.from_keras_model(qat_model)\n",
        "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
        "qat_tflite_model = converter.convert()\n",
        "qat_model_file = 'qat_model.tflite'\n",
        "# Save the model.\n",
        "with open(qat_model_file, 'wb') as f:\n",
        "    f.write(qat_tflite_model)\n",
        "    \n",
        "# CQAT model\n",
        "converter = tf.lite.TFLiteConverter.from_keras_model(cqat_model)\n",
        "converter.optimizations = [tf.lite.Optimize.DEFAULT]\n",
        "cqat_tflite_model = converter.convert()\n",
        "cqat_model_file = 'cqat_model.tflite'\n",
        "# Save the model.\n",
        "with open(cqat_model_file, 'wb') as f:\n",
        "    f.write(cqat_tflite_model)\n",
        "    \n",
        "print(\"QAT model size: \", get_gzipped_model_size(qat_model_file), ' KB')\n",
        "print(\"CQAT model size: \", get_gzipped_model_size(cqat_model_file), ' KB')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-zwAgK4SR2rw"
      },
      "source": [
        "## TF에서 TFLite까지의 정확도 지속성 확인\n",
        "\n",
        "테스트 데이터 세트에서 TFLite 모델을 평가하는 도우미 함수를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BEeTH_qBR2rw"
      },
      "outputs": [],
      "source": [
        "def eval_model(interpreter):\n",
        "  input_index = interpreter.get_input_details()[0][\"index\"]\n",
        "  output_index = interpreter.get_output_details()[0][\"index\"]\n",
        "\n",
        "  # Run predictions on every image in the \"test\" dataset.\n",
        "  prediction_digits = []\n",
        "  for i, test_image in enumerate(test_images):\n",
        "    if i % 1000 == 0:\n",
        "      print(f\"Evaluated on {i} results so far.\")\n",
        "    # Pre-processing: add batch dimension and convert to float32 to match with\n",
        "    # the model's input data format.\n",
        "    test_image = np.expand_dims(test_image, axis=0).astype(np.float32)\n",
        "    interpreter.set_tensor(input_index, test_image)\n",
        "\n",
        "    # Run inference.\n",
        "    interpreter.invoke()\n",
        "\n",
        "    # Post-processing: remove batch dimension and find the digit with highest\n",
        "    # probability.\n",
        "    output = interpreter.tensor(output_index)\n",
        "    digit = np.argmax(output()[0])\n",
        "    prediction_digits.append(digit)\n",
        "\n",
        "  print('\\n')\n",
        "  # Compare prediction results with ground truth labels to calculate accuracy.\n",
        "  prediction_digits = np.array(prediction_digits)\n",
        "  accuracy = (prediction_digits == test_labels).mean()\n",
        "  return accuracy"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CiL7XluNR2rx"
      },
      "source": [
        "클러스터링되고 양자화된 모델을 평가한 다음 TensorFlow의 정확도가 TFLite 백엔드에서 지속되는지 확인합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LLHIyrumR2rx"
      },
      "outputs": [],
      "source": [
        "interpreter = tf.lite.Interpreter(cqat_model_file)\n",
        "interpreter.allocate_tensors()\n",
        "\n",
        "cqat_test_accuracy = eval_model(interpreter)\n",
        "\n",
        "print('Clustered and quantized TFLite test_accuracy:', cqat_test_accuracy)\n",
        "print('Clustered TF test accuracy:', clustered_model_accuracy)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YokQ0TuvR2ry"
      },
      "source": [
        "## 훈련 후 양자화 적용 및 CQAT 모델과 비교\n",
        "\n",
        "다음으로, 클러스터링 된 모델에서 사후 훈련 양자화(미세 조정 없음)를 사용하고 CQAT 모델과 비교하여 정확성을 확인합니다. 이는 양자화된 모델의 정확성 향상을 위해 CQAT를 사용해야 하는 이유를 보여줍니다. 이 차이는 MNIST 모델이 상당히 작고 과잉 파라미터화되어 아주 잘 보이지 않을지도 모릅니다.\n",
        "\n",
        "먼저 처음 1000개의 훈련 이미지에서 캘리브레이션 데이터 세트에 대한 생성기를 정의합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "LoVVjF-zR2ry"
      },
      "outputs": [],
      "source": [
        "def mnist_representative_data_gen():\n",
        "  for image in train_images[:1000]:  \n",
        "    image = np.expand_dims(image, axis=0).astype(np.float32)\n",
        "    yield [image]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "KS6MMKVFR2ry"
      },
      "source": [
        "모델을 양자화하고 이전에 획득한 CQAT 모델과 정확도를 비교합니다. 미세 조정으로 양자화된 모델은 더 높은 정확도를 달성합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
{'fraction_non_alphanumeric': 0.1642534834134973, 'fraction_numerical': 0.008975058263499777, 'mean_word_length': 1.905979827089337, 'pattern_counts': {'":': 406, '<': 47, '<?xml version=': 0, '>': 47, 'https://': 28, 'lorem ipsum': 0, 'www.': 20, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '371967', 'n_tokens_mistral': 16507, 'n_tokens_neox': 17666, 'n_words': 2560}