Voila

DONGJIN IM·2022년 7월 10일
0

Product Serving 이론

목록 보기
3/10

Voila

Voila란?

  • 프로토타입을 위해 활용하는 도구

  • 단 시간내에 프로토타입을 만들 수 있어 자주 활용

  • 본래 목적 : Dashboard의 형성

Voila의 장점

  • Notebook에서 별도 코드 추가 없이 실행할 수 있음

  • Jupyter Notebook 결과를 쉽게 웹 형태로 띄울 수 있음

  • ipwidget, ipyleaflet 등에 사용 가능

  • Jupyter Notebook의 Extension이 있어 노트북에서 바로 대시보드로 변환 가능

  • Python, Julia, C++ 코드 지원

  • 고유한 템플릿 생성 가능

  • 쉬운 러닝커브


Voila 사용법

설치 및 실행

  • Voila 설치
pip install voila
  • Jupyter Notebook이나 Jupyter Server를 사용할 경우 실행 방법
    • Jupyter Notebook으로 실행할 경우 404 Not Found Error가 발생했음
    • 해결책 : https://forums.fast.ai/t/voila-for-jupyter-notebooks-bear-classifier-lesson-2/78393/6
    • 위 사이트의 해결책은, 한 마디로 Jupyter Notebook이 아닌 Jupyter Lab 형식을 통해 Voila를 실행하는 것이다
    • 나는 CLI를 통한 해결법도 수행시켜봤는데, 2가지 해결책에 대한 설명을 모두 써 놓았다.
  • 방법 1 : Jupyter Lab 실행시키기
    # 1번 과정 : Jupyter Notebook을 Jupyter Lab으로 수행시킴
    jupyter-lab --VoilaConfiguration.enable_nbextensions=True
    # CLI 명령어
    • 가장 오른쪽 노란색 그래프가 Voila를 수행시키게 해주는 버튼
    • 위 버튼을 클릭하면 Voila를 통해 Dashboard를 형성해준다
    • 실행 결과

  • 방법 2 : CLI를 통해 직접 실행
    voila {ipynb 파일 이름}.ipynb --enable_nbextensions=True
    • 실행 결과
    • 새로 고침을 하면 ipynb 파일을 변경한 점을 적용시켜 Update시킨 이후 Voila로 Dashboard를 형성함
  • CLI 실행의 장점

    물론 CLI는 귀찮을 수도 있다
    하지만, CLI만의 장점도 가지고 있는데, 가장 큰 장점은 내가 원하는 설정값을 인자를 통해 전달해줄 수 있다는 점이다.
    Jupyter Notebook이나 Jupter Lab에서는 Voila를 실행시킬 때 직접 인자를 건네주기 어렵지만, CLI에서는 가능하다.
    예를 들어, --strip_sources=False 를 활용하면 Voila에서 코드까지 보여주는 dashboard를 형성할 수 있게 된다

Voila Tip

  • cull 옵션의 활용
    • Voila 노트북을 활용하지 않을때는 자동으로 종료해야 리소스를 잡아먹지 않게 됨
    • idle 상태인 경우 cull, 즉 끄는 행위를 수행하도록 옵션을 줄 수 있음
    • CLI 옵션
      • MappingKernelManager.cull_interval : idle 커널을 확인할 시간 간격
      • MappingKernelManager.cull_idle_timeout : 커널을 idle 상태로 판단할 시간 간격. 설정 시간 내에 Update가 없을 경우 idle로 판단
      • 단위 : 초
  • 타임아웃
    • 아무 설정을 하지 않았을 경우 하나의 Cell이 30초 이상 진행될 때 Timeout Error가 발생
    • 무거운 연산(데이터 전처리, 예측 등)에서는 수행 중 종료될 수 있어서 긴 실행시간을 가졌다면 Timeout 제한시간을 늘려야 함
    • CLI 옵션
      • 'ExecutePreprocessor.timeout : 지정한 시간(단위 : 초)를 타임아웃으로 설정

프로토타입에 암호 설정하기

  1. Jupyter Notebook 설정 파일 생성하기
jupyter notebook --generate-config
  • 실행 시키면 Config 파일을 저장한 경로 및 이름이 나올텐데, 이 값을 꼭 기억하고 있어야 함
  1. Terminal(CLI)에서 아래 코드 실행
python
# python 실행시키기

from IPython.lib import passwd
passwd()
  • 실행 시킨 이후 내가 원하는 Password 입력 및 재확인
  • 이후 sha1:~으로 암호가 나타날텐데, sha1:~ 모든 값을 복사
  1. 1번에서 생성한 Config 파일 진입 및 값 변경
    • vi 명령어를 통해 수정하고, c.NotebookApp.password를 파일 내에서 찾아야 함
    • Config 파일 수정 방법
~~~
c.NotebookApp.password = {2번에서 복사한 값 붙여넣기}
~~~
  1. 이후 Voila를 수행시키면 암호를 입력해야지만 수행 가능하게 됨

ipywidget

ipywidget이란?

  • Interactive한 효과를 줄 수 있게 도와주는 라이브러리

import

import ipywidgets as widgets
from IPython.display import display
  • display : Python Object를 보여주는 함수

IntSlider

  • 정수형 Slider

  • 간단한 실행 방법

    • 아래 명령어에서 int_slider이라는 인자로 받지 말고 바로 widgets.IntSlider()로 수행시켜도 같은 결과가 나온다. 하지만, display() 명령어를 통해 내가 무엇을 하겠다는 것을 명시해주는 것이 더 좋은 방식
int_slider = widgets.IntSlider()
display(int_slider)
  • 인자(FloatSlider 등도 유사)
    • value : Default 값
    • step : 한번에 이동할 단계
    • orientation : 수직 / 수평선
      • 수직 : 'vertical'
      • 수평 : 'horizontal'
    • description : Slider의 Label
    • min, max : Slider의 최소, 최댓값

IntSlider 실행

int_slider1 = widgets.IntSlider(
    value=7,
    min=0,
    max=10,
    step=1,
    orientation='horizontal',
    description='Horizontal'
)

int_slider2 = widgets.IntSlider(
    value=7,
    min=0,
    max=10,
    step=1,
    orientation='vertical',
    description='Vertical'
)

display(int_slider1)
display(int_slider2)

  • IntRangeSlider

    • IntSlider의 "범위" 버전
    • value = [a,b]로 범위로 지정하며, 왼쪽과 오른쪽을 움직여 범위를 변경시켜줄 수 있음
  • int_widget.value

    • widgets.IntSlider()int_widget이라는 객체에 저장하였을 경우 활용할 수 있는 명령어
    • 현재 Int Slider가 가지고 있는 Value를 출력하거나, Int Slider의 Value를 지정해 줄 수 있다

IntRangeSlider 실행

range_slider = widgets.IntRangeSlider(
    value = [4,6],
    min = 0,
    max = 10,
    step = 1,
    description="Range"
)

display(range_slider)

TextWidget

  • Slider가 아닌 Input을 입력

    • Bounded : 범위가 주어진 Text
  • .value를 통해 값 접근 가능

TextWidget 실행 - Int 입력

bound_text = widgets.BoundedIntText(
    value = 7,
    min = 0,
    max = 10,
    step = 1,
    description='Text',
    disabled=False
)

int_text = widgets.IntText(
    value = 10,
    description = 'Any',
    disabled=False
)

display(bound_text)
display(int_text)
  • 인자 정리
    • disabled : True로 값을 설정할 경우, 처음 지정한 value에서 값을 변경할 수 없음
    • step : 한 번의 클릭으로 값을 얼마나 Up(혹은 Down) 시킬 것인지 정함

  • 위 사진을 보면, 화살표를 통해 값을 변경할 수도 있고 직접 값을 입력해줄 수도 있음을 알 수 있음

TextWidget 실행 - String 입력

text = widgets.Text(
    value = "hello world",
    placeholder = 'Type Something',
    description='default text'
)

text_area = widgets.Textarea(
    placeholder = 'Type Something',
    description='text area'  
)

password = widgets.Password(
    value = "hello world2",
    placeholder = 'Type Something',
    description='password'  
)

display(text)
display(text_area)
display(password)

  • 함수 설명

    • Text : 우리가 일반적으로 알고 있는 텍스트 입력 형식. "한 줄"에만 입력할 수 있음
    • Textarea : 글을 입력하는 공간을 확장(오른쪽 아래 클릭 후 드래그) 및 축소 할 수 있으며, 여러 줄로 입력값을 입력할 수 있음
    • Password : 패스워드 입력하듯이 입력한 값이 출력되지 않고 까만색 점으로 보임
  • 인자 설명

    • value : 처음에 Default로 띄워질 Value
    • placeholder : value가 None일 때, 즉 아무 값도 입력되어 있지 않을 때 띄울 값.
      • text_area 예시를 보면 value 설정이 없으므로 placeholder의 문장이 띄워짐을 볼 수 있음

Boolean Widget

  • True, False를 표시할 수 있는 Widget

  • ToggleButton

    • button_style : 버튼의 스타일
    • icon : 아이콘 모양 설정
  • CheckBox

    • indent : 들여쓰기 여부. True나 False로 값 지정

Toggle Button 실행

default_btn_false = widgets.ToggleButton(
    value=False,
    description='Click',
    button_style='',
    icon='check'
)

default_btn_true = widgets.ToggleButton(
    value=True,
    description='Click',
    button_style='',
    icon='check'
)

success_btn = widgets.ToggleButton(
    value=False,
    description='Click',
    button_style='success',
    icon='check'
)

display(default_btn_false)
display(default_btn_true)
display(success_btn)

  • 인자 설명
    • value : True일 경우 선택이 된 상태, False인 경우 클릭이 되지 않은 경우를 의미
    • button_style : 버튼의 스타일을 정해주는 것
      • '' : Default
      • 'success' : 성공 버튼(초록색)
      • 'info' : 파란색 버튼
      • 'danger' : 빨간색 버튼
      • 'warning' : 노란색 버튼
    • icon : Description 앞에 붙여지는 Icon 형식

CheckBox 실행

check_box = widgets.Checkbox(
    value = False,
    description='Check',
    indent=False
)

display(check_box)

  • 인자 설명
    • value : False일 경우 선택이 안 된 상태, True인 경우 선택이 된 상태를 의미함
    • indent : 들여쓰기 여부. False / True로 값 지정

Selection Widget

  • 값을 "선택"할 수 있는 Widget

  • Dropdown

    • 여러 가지 정해진 Value 리스트 중 한 개를 고를 수 있는 것
  • RadioButtons

    • 여러 가지 Option을 한 꺼번에 보내주고, 그 중 한 개의 Radio Button을 클릭하는 것
widgets.Dropdown(
    options=[1,2,3], 
    value=2,
    description='Number',
    diabled=False
)

  • options : 선택할 수 있는 답들을 리스트 형식으로 지정
    • 이 때 options 리스트를 (key, value)의 dict type list로 표현해도 됨
    • Key에 대응하는 값 : User가 볼 수 있는 값
      Value에 대응하는 값 : 실제 Value 값
    • 예시 코드 : options=[('One',1),('Two', 2),('Three', 3)]
    • 예시 코드에 대한 결과
      • One을 클릭하면, One과 대응되는 1 값이 선택된 것과 동일함

RadioButton 코드

widgets.RadioButtons(
    options = ['A','B','C'],
    description='Alphabet'
)

  • Dropdown과 구현 방식은 완전히 동일

Upload Widget

  • 파일을 업로드하는 Widget

실행

widgets.FileUpload(
    accept='',
    multiple=False
)

  • 인자 설명
    • accept : 허용할 확장자. 아무 값도 입력하지 않으면 모든 확장자 업로드를 허용함
    • multiple : 동시에 여러 파일을 업로드 할 수 있을지에 대한 설정. True일 경우 여러 파일을 동시에 업로드할 수 있음

Image Widget

  • 이미지를 보여주는 widget

실행

file = open('image.jpg', 'rb')
image = file.read()

widgets.Image(
    value=image,
    format='jpg',
    width=300,
    height=400
)

  • 인자 설명
    • value : 이미지 파일을 Byte 파일로 읽은 데이터
    • format : 이미지 데이터 형식
    • width / height : 너비 / 높이

Date Picker Widget

  • Date를 선택하는 Widget

  • Date, Time, DateTime도 존재

실행

widgets.DatePicker(
    description = 'Pick a Date',
    diabled=False
)

Widget Events

  • on_click

    • 버튼이 클릭되었을 때 함수가 동작하길 원하는 경우 on_click인자에 실행시키고 싶은 함수를 넘겨주면 됨
  • observe

    • 위젯 값이 변경될 때 실행시키고 싶은 함수가 있을 경우 활용함
    • on_click과 마찬가지로 인자로 함수를 넘겨주면 됨

on_click 코드

button = widgets.Button(description = 'Click')
output = widgets.Output()

display(button, output)

def on_button_clicked(button):
    with output:
        print("Hello world!")
        print(output)

button.on_click(on_button_clicked)

  • widgets.Output()

    아마 widgets Event에서 가장 핵심인 부분이 아닐까 싶다.
    이전에는 내가 원하는 Widget만 display 시키면 되었지만, 이벤트를 적용시키고 싶을 때는 display(button, output)을 통해 2개를 출력하는 것을 볼 수 있다.
    그렇다면 output은 무엇을 의마할까? print(output)을 통해 출력해 보았을 때, msg_id로써 특정 신호가 갔다는 것을 알 수 있다.
    즉, widgets.Output은 display를 통해 연결된 ipywidgets 요소에서부터 메시지가 발생(혹은 이벤트가 발생) 할 경우, 메시지를 전달받는 객체라고 생각하면 된다.
    with output: 이라는 의미는, output에 메시지가 발생했다는 의미이다.
    즉, Event가 발생했다는 의미이므로 내가 원하는 함수를 실행시키면 되는 것이다.
    여기서 재미있는 점은, widgets.Output은 단순히 현재 이벤트만 출력하는 것이 아닌 "이전 출력 내용"이 축적되는 방식으로 저장한다는 것이다. Observe에서 더 자세히 다루겠다

observe 실행

int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(output2)
        
int_range.observe(on_value_change)

일단 제대로 된 활용법을 익히기 전에 widgets.Output() 부터 확실히 잡고 가자.
위 메서드는 IntSlider의 값이 변경될 때마다 함수가 실행되는 것이다.

그렇다면 출력값을 통해 widgets.Output()을 알 수 있을 것이다

뭔가 복잡하다. 하지만 중요한 점은 4번째 Output만 보면 된다.
나중에 change를 출력하면 알겠지만, IntSlider가 변경될 경우 총 3개의 Message가 전달된다.
즉, 처음 3개의 Message는 사실 1개의 Event에서 발생했다는 의미이다.
따라서, 처음 Output 3개를 출력했고, 이 것이 "첫번째 Event"에 대한 Output이다.

이후 4번째 Output을 보자. 4번째 Output의 outputs에는 총 3개의 Output이 \n으로 연결되어 있는데, 해당 msg_id가 맨 위 3개 msg_id와 일치함을 알 수 있다.

즉, Output은 이전에 출력했던 내용들을 outputs라는 곳에 저장하고, 현재 발생하는 메시지는 msg_id에 새로 발생시키는 형식으로 Output을 형성하는 것이다.

그렇다면 제대로 observer를 활용해보자

int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(change)
        
int_range.observe(on_value_change)

위에서 말했듯 IntRange에 대한 변화는 총 3개의 Message를 발생시키는데, 위 이미지를 보면 _property_lock에 대한 것 2개, value에 대한 것 1개이다.

그런데 우리는 저 모든 Output이 필요 없고, 사실 'new', 즉 현재 갱신된 값만 존재하면 된다.

따라서, change['new']값만 받아오면 될 것이다

int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(change['new'])
        
int_range.observe(on_value_change)

거의 다 왔다. 이제 우리는 값이 6으로 변경시켰을 때 3개의 Output을 출력함을 알 수 있고, 위 3개는 바뀐(갱신된) 값에 대한 정보라는 것을 알 수 있다.

문제는, 무엇이 필요할지는 모르겠지만 대부분의 경우 2번째 출력값만 알면 Value 또한 알 수 있게 되는데, 굳이 3개의 출력문이 나온다는 점이다.

발생되는 3개의 Message에 대해 다시 한 번 잘 관찰해보자.
우리는 여기서 _property_lock에 대한 Value를 지우고 싶고, value라는 name을 가진 값에 대해서만 출력하고 싶음을 알 수 있다.

이 떄 활용할 수 있는 Parameter는 names이다.
names = X로 지정한다면, 여러 Message 중 'name':'X'인 메시지만 뽑아낼 수 있게 된다.

따라서, 최종적으로 아래와 같은 코드를 짜면 우리가 원하는, 값이 변경될 때 마다 1개의 이벤트가 발생하도록 만들 수 있을 것이다.

int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(change['new'])
        
int_range.observe(on_value_change, names='value')

Interact Decorator

  • @interact 데코레이터를 활용해 UI 컨트롤러를 자동 생성시킬 수 있음

  • 함수의 인자를 받아 인자에 적절한 UI가 생성됨

  • UI에 나타내고 싶지 않은 값 같은 경우 fixed()를 활용할 수 있음

Interact Decotartor 활용

from ipywidgets import interact, fixed
# ipywidgets에서 따로 import 하여 활용

@interact(x=True, y=1.0, z=fixed(10))
def g(x,y,z):
    return (x,y,z)

print('='*100)

@interact(x=True, y=1.0, z=10)
def h(x,y,z):
    return (x,y,z)

  • 위 g(x,y,z)는 z가 fixed(10)으로써 값이 10으로 고정되어 있으므로 z에 대한 ipywidget은 출력되지 않음

  • 반대로 (x,y,z)는 y와 z가 모두 fixed()가 아니므로 변경 가능한 intslider로 변환됨

  • x = True이므로, true / false를 표현해 줄 수 있는 버튼 형식으로 변환됨

Layout

  • 위젯의 레이아웃을 구성하고 싶을 경우 VBox, HBox를 활용할 수 있음

Layout 활용 코드

from ipywidgets import Button, HBox, VBox

words = ['left-up', 'right-up', 'left-down', 'right-down']
items = [Button(description=w) for w in words]
left_box = VBox([items[0], items[2]])
right_box = VBox([items[1], items[3]])

HBox([left_box, right_box])

  • 위 사진처럼 결과가 나오게 된 이유

    먼저 VBox를 통해 'left-up', 'left-down'을 "수직"으로 연결시킨다. 동일하게 'right-up', 'right-down'도 "수직"으로 연결시킨다.
    이렇게 수직으로 연결된 2개의 item을 HBox 명령어를 통해 Horizontal(수평) 적으로 합친다.
    그렇게 되면 2*1 형식의 테이블 2개를 수평 방향으로 합치게 되어 최종적으로 2*2 형식의 테이블로 만들어지는 것이다

profile
개념부터 확실히!

0개의 댓글