스프링 입문 맛보기 6

Spring 정리 2021. 8. 4. 20:54

인프런 강의 14일차.

 - 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한 강사님)

 

* 스프링 통합 테스트 (스프링 컨테이너와 DB까지 연결한 통합 테스트를 진행해보자)

 1.회원 서비스 스프링 통합 테스트

 - 테스트는 제일 끝단이기 때문에 편한 방법으로 선언해서 사용하면 됨.

 - @SpringBootTest,@Transacitional 태그를 사용하여 선언

 - @SpringBootTest : 스프링 컨테이너와 테스트를 함께 실행한다. (스프링 서버를 새로 시작 후 테스트가 완료되면 서버가 종료된다. )

 - @Transactional : 테스트케이스에 이 애노테이션이 있으면, 테스트 시작 전에 트랜잭션을 시작하고, 테스트 완료 후에 항상 롤백한다. (이렇게하면 DB에 데이터가 남지 않으므로 다음 테스트에 영향을 주지 않는다.)

 

'Spring 정리' 카테고리의 다른 글

스프링 입문 맛보기 8  (0) 2021.08.05
스프링 입문 맛보기 7  (0) 2021.08.05
스프링 입문 맛보기 5  (0) 2021.07.14
스프링 입문 맛보기 4  (0) 2021.07.06
스프링 입문 맛보기 3  (0) 2021.05.04

base64 서명 데이터 학습

Tensorflow 정리 2021. 7. 20. 13:53

1. Base64 인코딩 된 것 이미지로 전환해서 학습이 가능한지 확인.

 - base64 데이터 형태로 학습이 가능하다.

 

2. base64 데이터 학습 사용법 (Keras 모델의 Xception 사용)

 - tensorflow가 아직 익숙하지 않음로 Keras를 사용하여 구현.

 2.1 keras 모델 학습을 위한 전제조건

from tensorflow import keras

model = keras.applications.Xception(weights="imagenet")
model.compile(loss="categorical_crossentropy")

 2.2 작업 순서

   - 1. base64 문자열로 인코딩된 입력 이미지 디코딩
   - 2. Tensor로 변환
   - 3. 크기 조정
   - 4. 모델에 필요한 영상 전처리 단계 적용

from typing import Callable, Tuple
import tensorflow as tf


# if using tf >=2.0, disable eager execution to use tf.placeholder
tf.compat.v1.disable_eager_execution()


class ServingInputReceiver:
    """
    A callable object that returns a 
    `tf.estimator.export.ServingInputReceiver` 
    object that provides a method to convert 
    `image_bytes` input to model.input
    """
    def __init__(
        self, img_size: Tuple[int], 
        preprocess_fn: Callable = None, 
        input_name: str = "input_1"):
        
        self.img_size = img_size
        self.preprocess_fn = preprocess_fn
        self.input_name = input_name
        
    def decode_img_bytes(self, img_b64: str) -> tf.Tensor:
        """
        Decodes a base64 encoded bytes and converts it to a Tensor.
        Args:
            img_bytes (str): base64 encoded bytes of an image file
        Returns:
            img (Tensor): a tensor of shape (width, height, 3)
        """
        img = tf.io.decode_image(
            img_b64, 
            channels=3,
            dtype=tf.uint8,
            expand_animations=False
        )
        img = tf.image.resize(img, size=self.img_size)
        img = tf.ensure_shape(img, (*self.img_size, 3))
        img = tf.cast(img, tf.float32)
        return img
    
    def __call__(self) -> tf.estimator.export.ServingInputReceiver:
        # a placeholder for a batch of base64 string encoded of image bytes
        imgs_b64 = tf.compat.v1.placeholder(
            shape=(None,), 
            dtype=tf.string, 
            name="image_bytes")
        
        # apply self.decode_img_bytes() to a batch of image bytes (imgs_b64)
        imgs = tf.map_fn(
            self.decode_img_bytes, 
            imgs_b64, 
            dtype=tf.float32)
        
        # apply preprocess_fn if applicable
        if self.preprocess_fn:
            imgs = self.preprocess_fn(imgs)
        
        return tf.estimator.export.ServingInputReceiver(
            features={self.input_name: imgs},
            receiver_tensors={"image_bytes": imgs_b64}
        )

 2.3 객체 생성

   - base64 input을 처리할 serving_input_receiver객체 생성

   - 객체 생성 후 리사이즈 & 모델 적용 (keras.applications.xception.preprocess_input)

from tensorflow import keras

serving_input_receiver = ServingInputReceiver(
    img_size=(299, 299),
    preprocess_fn=keras.applications.xception.preprocess_input,
    input_name="input_1")

  2.4 TF 서비스 모델 export

estimator_save_dir = "estimators/xception"
estimator = tf.keras.estimator.model_to_estimator(
    keras_model=model,
    model_dir=estimator_save_dir)

export_model_dir = "models/classification/xception/"  # this is where the exported model will end up. 
estimator.export_saved_model(
    export_dir_base=export_model_dir,
    serving_input_receiver_fn=serving_input_receiver)

   - 2.4 까지 적용했을 때의 Keras 모델 구조

models
└── classification
    └── xception
        └── 1586063263
            ├── saved_model.pb
            └── variables
                ├── variables.data-00000-of-00002
                ├── variables.data-00001-of-00002
                └── variables.index

  2.5 export한 모델 서비스하기

   - Docker 이용

docker run -p 8500:8500 \ 
           -p 8501:8501 \ 
           -v <path_to_model_dir>/models/classification/xception:/models/xception \
           -e MODEL_NAME=xception -t tensorflow/serving

  2.6 서비스한 모델 테스트

   - 샘플 예제

import base64
from typing import Dict
import requests


SERVER_URL = "http://localhost:8501/v1/models/xception:predict"


def encode_img(img_filename: str) -> str:
    with open(img_filename, "rb") as f:
        img_bytes = base64.b64encode(f.read())
    return img_bytes.decode("utf8")


def prepare_request(img_filename: str) -> Dict:
    img_bytes = encode_img(img_filename)
    req = {
        "instances": [
            {"image_bytes": {"b64": img_bytes}},
        ]
    }
    return req

def send_request(img_filename: str) -> Dict:
    predict_request = prepare_request(img_filename)
    response = requests.post(SERVER_URL, json=predict_request)
    response.raise_for_status()
    return response.json()

   - 샘플 예제 request 방법(파일 경로 및 확장자)

response = send_request("path-to-img.jpg|png|gif")

   - 샘플 예제 output

{'predictions': [[0.000263870839,
   0.000306949107,
   0.000248591678,
   0.000319328334,
   0.000141894678,
   0.000295011501,
   0.000241611619,
   ...]]
  }

 

3. 이미지 처리를 위한 다른 사용법

  - base64 형태가 아닌 img 로딩해서 사용

   3.1 이미지(local file) -> 예측

#이미지를 불러와서 모델 예측에 사용
fpath="image.jpg"
image_size = (224, 224)

img = tf.keras.preprocessing.image.load_img(
    fpath,
    grayscale=False,
    color_mode="rgb",
    target_size=IMAGE_SIZE,
    interpolation='bilinear')

input_array = tf.keras.preprocessing.image.img_to_array(img)
input_array[0][0] # output: array([167., 103.,  66.], dtype=float32)
pred = model_loaded.predict(np.expand_dims(input_array, axis = 0)/255)

   3.2 이미지(local file) -> base64변환 -> 예측

#이미지를 base64로 읽어와서 예측에 사용
fpath="image.jpg"

# convert image base64 to be sent
image_base64 = base64.b64encode(open(fpath, 'rb').read()).decode("utf-8")
data = json.dumps({'image': image_base64})

# on the server side I get `data` and I want to read the base64 to feed tensorflow model
image = base64.b64decode(json.loads(data)['image'])
image[0:5] # output: b'xffxd8xffxe0x00'

image = tf.io.decode_image(image, channels=3)
image = tf.image.resize(image, 
                        method="bilinear", 
                        size=IMAGE_SIZE)

input_array = np.array(image) 
input_array[0][0] # output array([163.3482  , 102.01786 ,  62.383926], dtype=float32)
pred = model.predict(np.expand_dims(input_array, axis=0))

   3.3 이미지(url) -> base64변환 -> 예측 소스는 추가 필요

# Download the image
IMAGE_URL = https://tensorflow.org/images/blogs/serving/cat.jpg
dl_request= requests.get(IMAGE_URL, stream=True)
dl_request.raise_for_status()
# Compose a JSON Predict request (send JPEG image in base64).
jpeg_bytes= base64.b64encode(dl_request.content).decode('utf-8')
predict_request= '{"instances" : [{"b64": "%s"}]}' % jpeg_bytes
 
#output : {"instances" : [{"b64": "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAKACiiigAooooAKKKKACiiigAooooA//Z"}]}

 

 

4. 1회성이 아닌 지속적 학습하기 위해 어떻게 구현해야 하는가?

   - 단순히 생각하기로는 학습 서버와 운영 서버를 따로 구성

   - 학습 서버는 입력된 base64 input에 대해 지속적으로 학습 수행

   - 학습 후 일정 주기마자 운영 서버에 배포

   - 머신러닝모델 학습배포 사이클 https://ichi.pro/ko/meosin-leoning-model-eul-peulodeogsyeon-e-baepohaneun-munje-245001997032627

 

5. 참고 json

#2. base64 데이터 표현 json
{ "b64": "base64 encoded string" }
  
#3. base64 데이터 표현 json(서명 정의 및 캡션 포함)
{   "signature_name": "classify_objects",
    "examples": 
    [
      {
        "image": { "b64": "aW1hZ2UgYnl0ZXM=" },
        "caption": "seaside"
      },
      {
        "image": { "b64": "YXdlc29tZSBpbWFnZSBieXRlcw==" },
        "caption": "mountains"
      }   
    ] 
}

 

 

6. 기존에 논의되었던 문장기준 한글 OCR 판별 참고자료

http://101.101.175.217:8080/static/aiocr/learning

 

 

 

 

 

https://medium.com/@sdanaipat/deploy-keras-models-using-tensorflow-serving-5b46b7d5e024

'Tensorflow 정리' 카테고리의 다른 글

머신러닝 모델 배포 방법  (0) 2021.06.29
SVM 알고리즘  (0) 2021.06.08
TensorFlow 연습  (0) 2021.05.19

스프링 입문 맛보기 5

Spring 정리 2021. 7. 14. 13:28

인프런 강의 5일차.

 - 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한 강사님)

 

* 회원 관리 예제 - 웹 MVC 개발

 1. MemberController.java

package hello.hellospring.controller;

import hello.hellospring.Service.MemberService;
import hello.hellospring.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.List;

@Controller
public class MemberController {

    //MemberService.java내에 @Service가 선언되어 있지 않으면 최소 서버 띄울 때 ComponentScan에서 에러가 발생한다!
    //에러 내용 : Consider defining a bean of type 'hello.hellospring.Service.MemberService' in your configuration.
    private final MemberService memberService;
/*
    //필드 주입
    @Autowired private MemberService memberService;

    //세터 주입
    @Autowired
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
*/
    //생성자 주입
    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }

    @GetMapping("/members/new")
    public String createForm(){
        return "members/createMemberForm";
    }

    @PostMapping("/members/new")
    public String create(MemberForm form){
        Member member = new Member();
        member.setName(form.getName());

        memberService.join(member);

        return "redirect:/";
    }

    @GetMapping("/members")
    public String list(Model model){
        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        return "members/memberList";
    }
}

 > createForm, create, list 메소드 추가

 2. MemberForm.java

package hello.hellospring.controller;

public class MemberForm {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

 3. home.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <div>
        <h1>Hello Spring</h1>
        <p>회원 기능</p>
        <p>
            <a href="/members/new">회원 가입</a>
            <a href="/members">회원 목록</a>
        </p>
    </div>
</div>
</body>
</html>

 > 최초 화면 폼

 

 4. createMemberForm.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <form action="/members/new" method="post">
        <div class="form-group">
            <label for="name">이름</label>
            <input type="text" id="name" name="name" placeholder="이름을 입력하세요">
        </div>
        <button type="submit">등록</button>
    </form>
</div>
</body>
</html>

 > 회원가입 폼

 

 5. memberList.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container">
    <div>
        <table>
            <thead>
            <tr>
                <th>#</th>
                <th>이름</th>
            </tr>
            </thead>
            <tbody>
            <tr th:each="member : ${members}">
                <td th:text="${member.id}"></td>
                <td th:text="${member.name}"></td>
            </tr>
            </tbody>
        </table>
    </div>
</div>
</body>
</html>

 > 회원 리스트 폼

 

 

'Spring 정리' 카테고리의 다른 글

스프링 입문 맛보기 7  (0) 2021.08.05
스프링 입문 맛보기 6  (0) 2021.08.04
스프링 입문 맛보기 4  (0) 2021.07.06
스프링 입문 맛보기 3  (0) 2021.05.04
스프링 입문 맛보기 2  (0) 2021.04.29