디지털 이모티콘 예측기: 노트북에 딥러닝 모델을 구축하는 사용자 인터페이스

33256 단어
JavaScript와 Python 사이에 데이터를 전달하여 Jupyter notebook에 디지털 이모티콘 예측기를 구축합니다.일반적으로 자바스크립트는 노트북의 데이터를 시각화하는 데 사용되지만, 깊이 있는 학습 모델의 전단/UI 원형 제작에도 사용된다.

왜 젠장맞게 노트북에 사용자 인터페이스를 구축하려고 합니까?


간단한 대답:

Share the Deep Learning model notebook with colleagues from different teams like Business, Data Science, Front End developer, DevOps for their opinion before the real software development takes place.


Jupyter notebook은 빠른 원형을 위해 디자인된 것으로 모든 사람들이 알고 있지만, 우리도 빠른 UI 원형을 만들 수 있다.많은 데이터 과학자들은 곧 그들이 가지고 있는 소량의 데이터로 대심도 학습 모델을 구축하기 시작했고, 심지어는 그들이 무엇을 개발하고 누구를 위해 개발하고 있는지도 고려하지 않았다🤷‍♂️. 저를 믿으세요. 당신이 만족하는 첫 번째 모델(정밀도 등 지표 기반)이 생산에 배치되지 않을 것입니다. 원인은 매우 많습니다. 예를 들어 추리 시간이 느리고 설비 지원이 없고 메모리를 차지하는 등입니다.
경험이 풍부한 프로젝트/제품 매니저도 때때로 그들이 진정으로 무엇을 원하는지 모르기 때문에 모든 참여자로부터 업무 절차나 파이프라인 협의를 먼저 시도해 보자. 그래도 일부 일은 빠뜨릴 수 있다."저는 데이터 과학자입니다. 제 일은 ML모델을 만드는 것뿐입니다. 적어도 제가 보기에는 올바른 마음가짐이 아닙니다."상업, 웹 개발, DevOps에 대한 지식을 습득하면 많은 디버깅 시간을 절약하거나 왜 당신이 구축한 모델/작업 흐름이 상업에 가장 적합한지 설명할 수 있습니다.가장 좋은 것은 이곳의 만사통이다.겸사겸사 한마디 하자면, 이것은 결코 네가 동료의 책임을 져야 한다는 것을 의미하지 않는다.너의 범위 밖에서 일어나고 있는 일을 알아봐라.이 글은 딥러닝 모델의 입력과 출력을 위한 간단한 UI를 만들어서 작업 흐름의 빈틈을 보고 생산성을 높이는 방법을 설명하는 예를 보여 줍니다.

신청하다.


만약 MNIST 데이터 집합에 익숙하지 않다면, 데이터 집합은 0에서 9까지 손으로 그린 그레이스케일 이미지를 포함하기 때문에 디지털 표정 예측기를 구축할 수 있다는 것만 알 수 있습니다.따라서 Google 사용자 인터페이스는 사용자가 숫자를 그리고 해당하는 이모티콘을 표시할 수 있도록 합니다.

그래, UI를 구축해야 한다고 좀 믿었는데, 지금은?


우선, 우리는 우리가 무엇을 건설할 것인지를 결정할 것이다.처음부터 만들기보다는 Django를 사용하지 않고 수첩에서 다시 만들기Digit Recognizer를 시도해 보십시오.우리가 선택한 용례는 매우 좋다. 왜냐하면 우리는 간단하게 사용할 수 없기 때문이다. 이것은 HTML과 자바스크립트의 세계를 깊이 연구하게 한다.장기적으로 보면, 이 노선은 무한한 능력, 무료 자원을 가지고 있기 때문에, 더욱 중요한 것은 실제의 전단에 더욱 가깝기 때문이다.물론 슬라이더만 원한다면 pywidgets로 스크롤하면 된다.백엔드를 먼저 설정한 다음 UI로 돌아갑니다.

디지털 이모티콘 인식기


나는 이미 pywidgets를 사용하여 CNN 심도 있는 학습 모델을 훈련시켰고 우리는 here에서 모델과 권중을 다운로드할 것이다.이것은 3개의 Conv 블록과 2개의 완전히 연결된 레이어가 있는 간단한 모델입니다.이 모델에 사용된 변환은 pytorchResize입니다.그림의 ToTensor 함수를 호출하면 예측된 숫자를 얻을 수 있습니다.
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision import transforms

# Digit Recognizer model definition
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=5)
        self.conv2 = nn.Conv2d(32, 32, kernel_size=5)
        self.conv3 = nn.Conv2d(32,64, kernel_size=5)
        self.fc1 = nn.Linear(3*3*64, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.dropout(x, p=0.5, training=self.training)
        x = F.relu(F.max_pool2d(self.conv2(x), 2))
        x = F.dropout(x, p=0.5, training=self.training)
        x = F.relu(F.max_pool2d(self.conv3(x),2))
        x = F.dropout(x, p=0.5, training=self.training)
        x = x.view(-1,3*3*64 )
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return x



# Predict the digit given the tensor
def predict(image, modelName = 'mnist_model.pth'):

    # Resize before converting the image to Tensor
    Trfms = transforms.Compose([transforms.Resize(28), transforms.ToTensor()])

    # Apply transformations and increase dim for mini-batch dimension
    imageTensor = Trfms(image).unsqueeze(0)

    # Download model from URL and load it
    model = torch.load(modelName)

    # Activate Eval mode
    model.eval()

    # Pass the image tensor by the model & return max index of output
    with torch.no_grad():
        out = model(imageTensor)
        return int(torch.max(out, 1)[1])
BASE64 이미지(사용자가 숫자를 그리는 캔버스 영역)를 PIL 이미지로 변환하려면 도우미 함수가 필요합니다.
import re, base64
from PIL import Image
from io import BytesIO

# Decode the image drawn by the user
def decodeImage(codec):

    # remove the front part of codec
    base64_data = re.sub('^data:image/.+;base64,', '', codec)

    # base64 decode
    byte_data = base64.b64decode(base64_data)

    # Convert to bytes
    image_data = BytesIO(byte_data)

    # Convert to image & convert to grayscale
    img = Image.open(image_data).convert('L')
    return img
이제 predictdecodeImage 두 함수를 하나의 단독 함수로 합치겠습니다.함수의 출력(emoji html 코드)은 자바스크립트를 통해 html 태그에 직접 전달할 수 있기 때문에 이런 방법은 더욱 간단하다.만약 곤혹스러우시다면 잠시만 기다려 주십시오. 다음 절에서는 일이 더욱 쉽게 이해될 것입니다.
# Decode the image and predict the value
def decode_predict(imgStr):

    # Decode the image
    image = decodeImage(imgStr)

    # Declare html codes for 0-9 emoji
    emojis = [ 
            "0️⃣",
             "1️⃣",
             "2️⃣",
             "3️⃣",
             "4️⃣",
             "5️⃣",
             "6️⃣",
             "7️⃣",
             "8️⃣",
             "9️⃣"
    ]

    # Call the predict function
    digit = predict(image)

    # get corresponding emoji
    return emojis[digit]

사용자 인터페이스


HTML과 CSS 부분은 매우 간단합니다. 자바스크립트는 사용자가 캔버스에 그림을 그릴 수 있도록 하고, 단추 predictPredict 를 통해 함수를 호출할 수 있습니다.Clear 버튼으로 캔버스 정리 및 결과 설정🤔 이모티콘다음 섹션에서는 버튼 JS 함수Clear를 설정합니다.
from IPython.display import HTML

html = """
<div class="outer">
        <! -- HEADER SECTION -->
    <div> 
        <h3 style="margin-left: 30px;"> Digit Emoji Predictor &#128640; </h3> 
        <br>
        <h7 style="margin-left: 40px;"> Draw a digit from &#48;&#65039;&#8419; - &#57;&#65039;&#8419;</h7>
    </div>

    <div>
           <! -- CANVAS TO DRAW THE DIGIT -->
        <canvas id="canvas" width="250" height="250" style="border:2px solid; float: left; border-radius: 5px; cursor: crosshair;">
        </canvas>

            <! -- SHOW PREDICTED DIGIT EMOJI-->
        <div class="wrapper1"> <p id="result">&#129300;</p></div>

            <! -- BUTTONS TO CALL DL MODEL & CLEAR THE CANVAS -->
        <div class="wrapper2">
            <button type="button" id="predictButton" style="color: #4CAF50;margin:10px;">  Predict </button>  
            <button type="button" id="clearButton" style="color: #f44336;margin:10px;">  Clear </button>  
        </div>
    </div>
</div>
"""


css = """
<style>
    .wrapper1 {
      text-align: center;
      display: inline-block;
      position: absolute;
      top: 82%;
      left: 25%;
      justify-content: center;
      font-size: 30px;
    }
    .wrapper2 {
      text-align: center;
      display: inline-block;
      position: absolute;
      top: 90%;
      left: 19%;
      justify-content: center;
    }

    .outer {
        height: 400px; 
        width: 400px;
        justify-content: center;
    }

</style>
"""


javascript = """

<script type="text/javascript">
(function() {
    /* SETUP CANVAS & ALLOW USER TO DRAW */
    var canvas = document.querySelector("#canvas");
    canvas.width = 250;
    canvas.height = 250;
    var context = canvas.getContext("2d");
    var canvastop = canvas.offsetTop
    var lastx;
    var lasty;
    context.strokeStyle = "#000000";
    context.lineCap = 'round';
    context.lineJoin = 'round';
    context.lineWidth = 5;

    function dot(x, y) {
        context.beginPath();
        context.fillStyle = "#000000";
        context.arc(x, y, 1, 0, Math.PI * 2, true);
        context.fill();
        context.stroke();
        context.closePath();
    }

    function line(fromx, fromy, tox, toy) {
        context.beginPath();
        context.moveTo(fromx, fromy);
        context.lineTo(tox, toy);
        context.stroke();
        context.closePath();
    }
    canvas.ontouchstart = function(event) {
        event.preventDefault();
        lastx = event.touches[0].clientX;
        lasty = event.touches[0].clientY - canvastop;
        dot(lastx, lasty);
    }
    canvas.ontouchmove = function(event) {
        event.preventDefault();
        var newx = event.touches[0].clientX;
        var newy = event.touches[0].clientY - canvastop;
        line(lastx, lasty, newx, newy);
        lastx = newx;
        lasty = newy;
    }
    var Mouse = {
        x: 0,
        y: 0
    };
    var lastMouse = {
        x: 0,
        y: 0
    };
    context.fillStyle = "white";
    context.fillRect(0, 0, canvas.width, canvas.height);
    context.color = "black";
    context.lineWidth = 10;
    context.lineJoin = context.lineCap = 'round';
    debug();
    canvas.addEventListener("mousemove", function(e) {
        lastMouse.x = Mouse.x;
        lastMouse.y = Mouse.y;
        Mouse.x = e.pageX - canvas.getBoundingClientRect().left;
        Mouse.y = e.pageY - canvas.getBoundingClientRect().top;
    }, false);
    canvas.addEventListener("mousedown", function(e) {
        canvas.addEventListener("mousemove", onPaint, false);
    }, false);
    canvas.addEventListener("mouseup", function() {
        canvas.removeEventListener("mousemove", onPaint, false);
    }, false);
    var onPaint = function() {
        context.lineWidth = context.lineWidth;
        context.lineJoin = "round";
        context.lineCap = "round";
        context.strokeStyle = context.color;
        context.beginPath();
        context.moveTo(lastMouse.x, lastMouse.y);
        context.lineTo(Mouse.x, Mouse.y);
        context.closePath();
        context.stroke();
    };

    function debug() {
        /* CLEAR BUTTON */
        var clearButton = $("#clearButton");
        clearButton.on("click", function() {
            context.clearRect(0, 0, 250, 250);
            context.fillStyle = "white";
            context.fillRect(0, 0, canvas.width, canvas.height);

            /* Remove Result */
            document.getElementById("result").innerHTML = "&#129300;";
        });
        $("#colors").change(function() {
            var color = $("#colors").val();
            context.color = color;
        });
        $("#lineWidth").change(function() {
            context.lineWidth = $(this).val();
        });
    }
}());

</script>

"""


HTML(html + css + javascript)

위대하다우리는 캔버스 내부에 그림을 그릴 수 있다. Predict 단추를 누르면 캔버스가 정리된다.사용자가 Clear 단추를 눌렀을 때, 까다로운 부분은 우리가 한 것처럼 시작되었다.

  • JS to Python 👉 캔버스에서 도형을 잡아python에 전달
  •     /* PASS CANVAS BASE64 IMAGE TO PYTHON VARIABLE imgStr*/
        var imgData = canvasObj.toDataURL();
        var imgVar = 'imgStr';
        var passImgCode = imgVar + " = '" + imgData + "'";
        var kernel = IPython.notebook.kernel;
        kernel.execute(passImgCode);
    

  • Python to JS 👉 예측된 이모티콘을 HTML 요소의 값으로 설정(Predict)
  •     /* CALL PYTHON FUNCTION "decode_predict" WITH "imgStr" AS ARGUMENT */
        function handle_output(response) {
            /* UPDATE THE HTML BASED ON THE OUTPUT */
            var result = response.content.data["text/plain"].slice(1, -1);
            document.getElementById("result").innerHTML = result;
        }
        var callbacks = {
            'iopub': {
                'output': handle_output,
            }
        };
        var getPredictionCode = "decode_predict(imgStr)";
        kernel.execute(getPredictionCode, callbacks, { silent: false });
    
    
    한데 연결되다.
    predictJS = """
    <script type="text/javascript">
    
    /* PREDICTION BUTTON */
    $("#predictButton").click(function() {
        var canvasObj = document.getElementById("canvas");
        var context = canvas.getContext("2d");
    
        /* PASS CANVAS BASE64 IMAGE TO PYTHON VARIABLE imgStr*/
        var imgData = canvasObj.toDataURL();
        var imgVar = 'imgStr';
        var passImgCode = imgVar + " = '" + imgData + "'";
        var kernel = IPython.notebook.kernel;
        kernel.execute(passImgCode);
    
        /* CALL PYTHON FUNCTION "decode_predict" WITH "imgStr" AS ARGUMENT */
        function handle_output(response) {
            /* UPDATE THE HTML BASED ON THE OUTPUT */
            var result = response.content.data["text/plain"].slice(1, -1);
            document.getElementById("result").innerHTML = result;
        }
        var callbacks = {
            'iopub': {
                'output': handle_output,
            }
        };
        var getPredictionCode = "decode_predict(imgStr)";
        kernel.execute(getPredictionCode, callbacks, { silent: false });
    });
    </script>
    
    """
    HTML(predictJS)
    

    우리는 좋은 소식과 나쁜 소식이 있습니다. 좋은 소식은 파이톤과 자바스크립트 사이의 데이터 연결이 정상적으로 작동하지만, 예측 값이 모두 잘못되었습니다. (같은 값-8에 비추었습니다.)python에 전달된 캔버스 이미지와 MNIST 데이터 세트 이미지를 비교해 봅시다.
    캔버스 이미지
    몬네스토 이미지


    모형은 검은색 배경과 흰색 획을 가진 데이터 집합에서 훈련된 것이기 때문에 우리는 #result 이미지가 필요하다는 것을 잘 알고 있다.
    # Invert the image as model expects black background & white strokes
    image = PIL.ImageOps.invert(image)
    
    import PIL.ImageOps
    
    # Decode the image and predict the value
    def decode_predict(imgStr):
    
        # Decode the image
        image = decodeImage(imgStr)
    
        # Invert the image as model expects black background & white strokes
        image = PIL.ImageOps.invert(image)
    
        # Declare html codes for 0-9 emoji
        emojis = [ 
                "&#48;&#65039;&#8419;",
                 "&#49;&#65039;&#8419;",
                 "&#50;&#65039;&#8419;",
                 "&#51;&#65039;&#8419;",
                 "&#52;&#65039;&#8419;",
                 "&#53;&#65039;&#8419;",
                 "&#54;&#65039;&#8419;",
                 "&#55;&#65039;&#8419;",
                 "&#56;&#65039;&#8419;",
                 "&#57;&#65039;&#8419;"
        ]
    
        # Call the predict function
        digit = predict(image)
    
        # get corresponding emoji
        return emojis[digit]
    

    경탄할 만한!그것이 작용했다.

    결론


    이제 여러분과 이 공책을 빨리 공유하고 의견을 구할 수 있습니다.이것은 초기의 원형이라는 것을 잊지 마라.우리는 먼지를 털지 않고 웹 응용 프로그램의 기능을 복제했다.가장 큰 장점은 우리가 노트북의 편안함을 떠나지 않고 훈련과 테스트 데이터 사이의 차이를 메울 수 있다는 것이다.
    기본적인 인터넷 개발 기술이 데이터 과학자에게 정말 도움이 된다는 것을 설득할 수 있기를 바랍니다.here에서 노트북에 액세스할 수 있습니다.언제든지 댓글이나 연락 주세요.

    좋은 웹페이지 즐겨찾기