머신러닝 엔지니어 실무-Section2-1

인프런 Chris Song님 강의 정리

Section2: 코드 품질, 데이터 검증, 모델 분석

리서치 코드 품질 관리

코드 품질 문제 유형

  • 각자 개인 컴퓨터에 저장되어 있음
  • 복사 붙여넣기로 개발된 코드로 인한 중복이 많다
  • 연구 결과 재연이 불가능
  • 너무 많은 전역 변수 (side-effect)
    --> 최대한 파라미터를 전달하고 받아오는 방식으로 고쳐야함..
  • 너무 긴 코드 (디버깅 불편, 함수와 클래스 구분이 명확하지 않음)
    --> python의 경우 500 line 미만으로 개발!
  • 상대 참조 (나중에 폴더 구조가 바뀌면, 디버깅이 어려워짐)
    --> PYTHONPATH 환경변수를 활용해서 시작 지점을 명확하게 하고, absolute import를 사용해야함
  • 명확하지 않은 변수명

깨진 유리창의 법칙

  • 만일 한 건물의 유리창이 깨어진 채로 방치되어 있다면, 곧 다른 유리창들도 깨어질 것
  • 코드 품질 문제도 좋지 않으면, 다른 사람들의 코드 품질도 안 좋아 질 것이다.

린트와 유닛테스트

  • 린트: Flake8
  • Python 타입 체크: mypy
    • 타입 힌트를 함수에 작성하는 것이 가독성과 에러를 방지할 수 있음

린트란? (강좌에 없는 내용)

개념

  • 문법 오류, 코드 컨벤션 등을 잡아주는 검사기
  • 의심스럽거나, 에러를 발생하기 쉬운 코드에 표시(flag)를 달아 놓음
  • 매번 코드 리뷰를 통해 코드 컨벤션을 체크할 수 있지만,
    모든 코드를 매번 체크하면서 컨벤션을 유지하는 것은 어려움
  • 각 언어 별로 이를 위한 도구가 존재하는데, 이를 Lint한다고 하는 것 같음

flake8

  • Python의 Lint 도구
  • PEP8을 기반으로 코드 컨벤션을 검사함
  • Flake8 Rules

  • 예시
# main.py

import os
def    helloworld(s):
    return f"Hello World! {s}"
if __name__ == '__main__':
    print(helloworld('Caleb'))

flake8 main.py

1) F401 --> os 라이브러리를 impor 해놓고 사용하지 않았다.
2) E302 --> 라이브러리를 import 했으면 두 줄은 띄워라
3) E271 --> helloworld 함수 명칭에 공백 여러개가 있다
4) E305 --> 함수나 클래스를 정의한 이후에 비어있는 2줄을 넣어라
5) W292 --> 파일의 마지막에는 공백 line을 삽입해라

  • 예시 수정
def helloworld(s):
    return f"Hello World! {s}"


if __name__ == '__main__':
    print(helloworld('Caleb'))
    

black

  • 코드의 가독성을 높여줌
  • 자동으로 re-format을 해줌
# Before
def       helloworld(a):
    print("Hello world! {a}!")#asdasd
if __name__ == "__main__":
    helloworld("Yang")


# After
# black main.py

def helloworld(a):
    print("Hello world! {a}!")  # asdasd


if __name__ == "__main__":
    helloworld("Yang")

타입 어노테이션/타입 체크란? (강좌에 없는 내용)

  • 파이썬의 동적(dynamic) 타입 처리는 일회성 script or 소규모 application을 개발할 때는 큰 장점으로 작용함
  • 하지만 application 규모가 커지게 되면, 다이나믹함이 치명적인 버그로 이어질 확률이 높음
    --> 이는 안정성에 위험 요소로 작용함
  • 타입 어노테이션(type annotation)은 파이썬 코드에 타입을 명시하기 위한 표준을 정립하기 위한 것
    • python 3.5부터 추가됨
  • 변수나 함수에 타입이 명시된 파이썬 코드는 정적 타입 검사기를 통해 코드를 실행하지 않고도 타입 에러를 찾아낼 수 있음

타입 검사

  • gi라는 변수의 타입을 int로 명시하기 위해 타입 어노테이션을 추가했다.
  • 아래 코드를 python으로 실행하면 문제 없이 돌아간다.
    • 타입 어노테이션은 언어 레벨에서는 코드 실행에 아무 영향이나 제약을 주지 않기 때문
  • 아래와 같이 타입 어노테이션을 사용하는 것을 타입 힌팅(type hinting)이라고 한다.
    • 주로 코드를 읽기 쉽게 하거나, 코드 편집기(IDE), 린터(linter)에서 활용됨
# test.py
# python test.py

gi: int="4"
print(gi)
  • 하지만 위 코드를 mypy로 실행해보면 에러가 발생한다.
    • 변수의 타입과 저장된 값의 타입이 다른 type error 발생
# test.py
# mypy test.py

gi: int="4"
print(gi)


  • mypy를 사용하면 python interpreter가 잡지 못하는 타입 버그를 찾아낼 수 있음

오류 검사

  • mypy를 사용하면 타입 자체의 버그 뿐만 아니라 실제 런타임에 발생할 수 있는 오류를 미리 알아낼 수도 있음
  • 아래 코드를 실행하면 gi 함수 두 번째 인자에 str타입이 넘어가기에 오류가 발생함
# test.py
# python test.py

def gi(msg: str, times: int=1) -> list:
    return [msg] * times

gi("Hi", "4")


  • 위 코드를 mypy로 돌려보면 미리 오류가 발생할 것임을 알려준다.
# test.py
# mypy test.py

def gi(msg: str, times: int=1) -> list:
    return [msg] * times

gi("Hi", "4")


지속적 통합 (CI)

  • Continuous integration
  • 소프트웨어의 질적 향상을 채크하고 배포 시간을 줄일 수 있음
  • Ex. Github Actions, Jenkins

CI란? (강좌에 없는 내용)

  • 팀의 구성원들이 작업한 내용을 정기적으로 통합하는 것
    • 각각의 팀멤버들로부터 Submit된 소스코드를 정기적으로 통합하는 것
  • CI tool: 위 내용을 시행해주는 프로그램

CI 시스템 구축

  • CI 시스템이 구축되지 않은 경우:
    • 개발자들이 각자 개발한 소스코드를 형상관리 서버에 커밋하면 별도의 품질관리를 거치지 않고, 대부분 개발이 끝난 막바지에 통합을 하여 테스트를 진행함
    • 이 경우, 잘못된 소스코드를 형상관리 시스템에 반영하였을 경우에 발생되는 문제가 개발 후반에 장애로 발견됨
  • CI 시스템이 구축된 경우:
    • CI서버는 형상관리 서버에 Commit된 소스코드를 주기적으로 폴링하여 컴파일, 단위테스트, 코드 인스펙션 등의 과정을 수행
      • 폴링:
        • 일정한 주기를 가지고 서버와 응답을 주고 받는 방식
        • 충돌 회피 또는 동기화 처리의 목적으로 다른 장치의 상태를 주기적으로 검사하여 일정한 조건을 만족할 떄 송수신 등의 자료처리를 함
      • 컴파일: 사람의 언어를 컴퓨터가 이해할 수 있는 언어로 바꿔주는 과정
      • 코드 인스펙션: 정의된 룰을 기반으로 소스 코드를 검사하여 오류 및 위험요인을 식별하여 알려주는 기능
    • 신규 or 수정된 소스코드가 결함이 있는지 여부를 지속적으로 검증함
    • 검증 결과는 이메일, RSS등의 피드백 매커니즘을 통해 개발자에게 전달
      • RSS: 블로그, 뉴스, 칼럼 등 업데이트가 잦은 콘텐츠들을 사용자가 편하게 볼 수 있도록 만들어진 규약
    • 조기 결함을 발견하여 해결 가능

Github Actions (강좌에 없는 내용)

개요

  • Github에서 제공하는 워크플로우(workflow)를 자동화하도록 도와주는 도구
  • 테스트, 빌드, 배포 등의 다양한 작업들을 자동화하여 처리
  • public 저장소의 경우 무료로 사용 가능하며 private 저장소는 월마다 제공되는 무료 사용량 초과 시에 요금이 부과
    • 무료 계정 기준 500MB의 스토리지와 월마다 2,000분 제공
  • Github 저장소에서 등록하거나 .github/workflows 폴더 내에 .yml파일을 추가하여 등록할 수 있음

Github Actions의 구성

  • 워크 플로우(workflows)
    • 저장소에 추가하는 자동화된 프로세스
    • 하나 이상의 job으로 이루어져 있으며, 이벤트에 의해 실행됨
  • 이벤트(Events)
    • 워크플로우를 실행하는 특정 활동이나 규칙
    • 커밋의 push, pull request가 생성되었을 때, 저장소 dispath event를 통해 Github 외부에서 발생하는 활동으로도 이벤트를 발생시킬 수 있음
    • schedule에 POSIX cron 문법으로 스케줄 이벤트를 발생시킬 수 있음
  • 러너(runners)
  • 작업(jobs)
    • 워크플로우의 기본 단위
    • 스텝으로 이루어져 있음
    • 워크플로우는 여러 작업을 병렬적으로 실행하며 순차적으로 실행하도록 설정 할 수 있음
      • 빌드와 테스트 코드 수행 작업을 순차적으로 실행 (빌드 실패 시, 테스트는 진행 실행되지 않음)
  • 스텝(steps)
    • 작업에서 커맨드를 실행하는 독립적인 단위
    • 한 작업의 각 스텝들은 동일한 러너에서 실행되므로 해당 작업의 액션들은 서로 데이터를 공유함
  • 액션(actions)
    • 워크플로우의 가장 작은 요소
    • 직접 만들어 사용할 수도 있고, 마켓에 등록된 만들어진 것을 가져와 사용할 수도 있음

워크 플로우 관리

  • 민감한 정보 저장
    • 비밀번호, 인증서 같은 정보는 Github에 secret으로 저장하여 환경변수로 사용이 가능함
  • 의존적인 작업 구성
    • 작업은 병렬적으로 수행됨
    • 다른 작업이 완전히 끝난 후에 작업을 실행시키고 싶다면 needs 키워드를 통해 작업이 의존성을 갖도록 지정하면 됨

  • 빌드 매트릭스 활용
    • 워크플로우가 다양한 OS, 플랫폼, 언어의 여러 조합에서 테스트를 실행하려는 경우 빌드 매트릭스를 활용하면 됨
    • 빌드 옵션을 배열로 받는 strategy 키워드를 사용하면 됨

  • 종속성 캐싱
    • GIthub의 러너는 각 작업에서 새로운 환경으로 실행됨
    • 작업들이 종속성을 재사용하는 경우 파일들을 캐싱하여 성능을 높이면 됨
    • 캐시를 생성하면 해당 저장소의 모든 워크플로우에서 사용 가능

Github Actions 적용사례


  • Github에서 Pull Request를 생성했을 때, 자동으로 Github이 다음과 같이 3가지 작업을 자동으로 수행시킴
    • 테스트 자동화: 유닛테스트를 돌린 후 결과를 Code Climate에 전송
    • 코드 Lint 체크: black & flake8

코딩 컨벤션 체크 자동화 Workflow

  • 코딩 컨벤션을 따라야하는 이유
    • 코드 가독성을 높여서 함께하는 팀원들에 대한 배려
    • 코드의 품질 및 코딩 속도 향상
    • 모든 팀원들이 동일한 스타일의 코드를 유지
  • Github repository에서 .github/workflows/lint.yml 파일을 생성
    • 아래와 같이 workflow를 설정해두면, 앞으로 코딩 컨벤션을 맞추지 못하면 Github가 PR merge를 차단함
    • main 브랜치에 본인의 코드를 머지하려면 코딩 컨벤션을 맞춰야만 함
name: Lint Code Base

on: push

jobs:
  # Set the job key. The key is displayed as the job name
  # when a job name is not provided
  super-lint:
    # Name the Job
    name: Lint Code Base
    # Set the type of machine to run on
    runs-on: ubuntu-latest

    env:
      OS: ${{ matrix.os }}
      PYTHON: '3.7'

    steps:
      # Checks out a copy of your repository on the ubuntu-latest machine
      - name: Checkout code
        uses: actions/checkout@v2

      # Runs the Super-Linter action
      - name: Lint Code Base
        uses: github/super-linter@v3
        env:
          DEFAULT_BRANCH: master
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          VALIDATE_PYTHON_BLACK: true
          VALIDATE_PYTHON_FLAKE8: true

테스트 자동화 Workflow

  • 테스트 자동화 시스템 구축 이유

    1. 설계 수정 시간의 단축
      • 설계 수정은 시간이 지날 수록 그 비용이 급격하게 증가함
      • 테스트를 먼저 설계하고 개발을 진행하게 되면, 각 함수의 구조와 기능 정의를 명확하게 하므로, 설계의 구조적 문제를 빠르게 찾아낼 수 있게됨
    2. 객체지향적인 코드 개발
      • 본격적인 개발에 앞서 테스트 코드를 먼저 작성한다면 좀 더 명확한 기능과 구조를 설계할 수 있음
    3. 디버깅 시간 단축
      • 모델의 인퍼런스 결과가 이상한 경우엔 그 원인이 어디로부터 왔는지 명확하게 이해하기 어려움
      • 데이터 전/후 처리에 테스트 코드를 작성하면, 모델의 인퍼런스 결과를 좀 더 신뢰할 수 있음
    4. 유지보수 용이성
      • 테스트 코드들을 작성하게 되면, 한 모듈의 기능 변화가 다른 모듈에 어떤 영향을 미치는 지 테스트 코드를 돌려봄으로써 바로 추적할 수 있게됨
      • 기능을 추가하더라도 사이드 이펙트에 대한 염려로부터 자유로워짐
  • Code Climate: 코드 품질 관리를 모니터링하도록 해주는 유료 솔루션 (오픈소스의 경우에는 무료)

    • 코드 품질 모니터링 대시보드 생성 가능
    • 코드의 결함이나 안좋은 패턴들을 감지하고 자동으로 코드 리뷰가 달림 --> 코드 리뷰 자동화 (생산성 향상)

참고

좋은 웹페이지 즐겨찾기