Python에 해당하는 글 2

[Llama2 receipt] Python Gradio를 이용한 WebUI 추가하기

AI|2024. 2. 6. 13:47

기존 여러 LLM관련 프로젝트에서는 gradio를 이용해서 WebUI를 지원했는데, Lama2 receipt에서는 아무리 찾아봐도 WebUI가 없어서 한번 만들어봤습니다. Gradio Python package를 처음 접했을 때 놀랐습니다. 이렇게 쉡게 웹 UI를 만들 수 있고 사람들에게 공유할 수 있구나하고요. 정말 Python 공동체는 이런것도 만들 생각을 하는구나 하는 생각도 했습니다. 당연하겠지만, 전문 프로그래머가 아닌 과학자나 공학자들이 좋아하는 언어로 웹서비스 구현에 시간을 쓸 이유가 없는거죠. 그냥 동작만하는 WebUI가 필요할 때, 바로 Gradio 패키지를 쓰면 됩니다.

일단, 해당 프로젝트에 Pull Request를 했는데, 다행히 리뷰도 받고 있어서 조만간 머지될 것 같습니다. 
코드는 다음과 같습니다.

example/inference.py

# Copyright (c) Meta Platforms, Inc. and affiliates.
# This software may be used and distributed according to the terms of the Llama 2 Community License Agreement.

# from accelerate import init_empty_weights, load_checkpoint_and_dispatch

import fire
import os
import sys
import time
import gradio as gr

import torch
from transformers import LlamaTokenizer

from llama_recipes.inference.safety_utils import get_safety_checker, AgentType
from llama_recipes.inference.model_utils import load_model, load_peft_model

from accelerate.utils import is_xpu_available

def main(
    model_name,
    peft_model: str=None,
    quantization: bool=False,
    max_new_tokens =100, #The maximum numbers of tokens to generate
    prompt_file: str=None,
    seed: int=42, #seed value for reproducibility
    do_sample: bool=True, #Whether or not to use sampling ; use greedy decoding otherwise.
    min_length: int=None, #The minimum length of the sequence to be generated, input prompt + min_new_tokens
    use_cache: bool=True,  #[optional] Whether or not the model should use the past last key/values attentions Whether or not the model should use the past last key/values attentions (if applicable to the model) to speed up decoding.
    top_p: float=1.0, # [optional] If set to float < 1, only the smallest set of most probable tokens with probabilities that add up to top_p or higher are kept for generation.
    temperature: float=1.0, # [optional] The value used to modulate the next token probabilities.
    top_k: int=50, # [optional] The number of highest probability vocabulary tokens to keep for top-k-filtering.
    repetition_penalty: float=1.0, #The parameter for repetition penalty. 1.0 means no penalty.
    length_penalty: int=1, #[optional] Exponential penalty to the length that is used with beam-based generation. 
    enable_azure_content_safety: bool=False, # Enable safety check with Azure content safety api
    enable_sensitive_topics: bool=False, # Enable check for sensitive topics using AuditNLG APIs
    enable_salesforce_content_safety: bool=True, # Enable safety check with Salesforce safety flan t5
    enable_llamaguard_content_safety: bool=False,
    max_padding_length: int=None, # the max padding length to be used with tokenizer padding the prompts.
    use_fast_kernels: bool = False, # Enable using SDPA from PyTroch Accelerated Transformers, make use Flash Attention and Xformer memory-efficient kernels
    **kwargs
):

  def evaluate(user_prompt, temperature, top_p, top_k, max_new_tokens, **kwargs,):
    safety_checker = get_safety_checker(enable_azure_content_safety,
                                        enable_sensitive_topics,
                                        enable_salesforce_content_safety,
                                        enable_llamaguard_content_safety
                                        )

    # Safety check of the user prompt
    safety_results = [check(user_prompt) for check in safety_checker]
    are_safe = all([r[1] for r in safety_results])
    if are_safe:
        print("User prompt deemed safe.")
        print(f"User prompt:\n{user_prompt}")
    else:
        print("User prompt deemed unsafe.")
        for method, is_safe, report in safety_results:
            if not is_safe:
                print(method)
                print(report)
        print("Skipping the inference as the prompt is not safe.")
        sys.exit(1)  # Exit the program with an error status

    # Set the seeds for reproducibility
    if is_xpu_available():
        torch.xpu.manual_seed(seed)
    else:
        torch.cuda.manual_seed(seed)
    torch.manual_seed(seed)
    
    model = load_model(model_name, quantization, use_fast_kernels)
    if peft_model:
        model = load_peft_model(model, peft_model)

    model.eval()
    

    tokenizer = LlamaTokenizer.from_pretrained(model_name)
    tokenizer.pad_token = tokenizer.eos_token
    
    batch = tokenizer(user_prompt, padding='max_length', truncation=True, max_length=max_padding_length, return_tensors="pt")
    if is_xpu_available():
        batch = {k: v.to("xpu") for k, v in batch.items()}
    else:
        batch = {k: v.to("cuda") for k, v in batch.items()}

    start = time.perf_counter()
    with torch.no_grad():
        outputs = model.generate(
            **batch,
            max_new_tokens=max_new_tokens,
            do_sample=do_sample,
            top_p=top_p,
            temperature=temperature,
            min_length=min_length,
            use_cache=use_cache,
            top_k=top_k,
            repetition_penalty=repetition_penalty,
            length_penalty=length_penalty,
            **kwargs 
        )
    e2e_inference_time = (time.perf_counter()-start)*1000
    print(f"the inference time is {e2e_inference_time} ms")
    output_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
    
    # Safety check of the model output
    safety_results = [check(output_text, agent_type=AgentType.AGENT, user_prompt=user_prompt) for check in safety_checker]
    are_safe = all([r[1] for r in safety_results])
    if are_safe:
        print("User input and model output deemed safe.")
        print(f"Model output:\n{output_text}")
    else:
        print("Model output deemed unsafe.")
        for method, is_safe, report in safety_results:
            if not is_safe:
                print(method)
                print(report)
    return output_text

  if prompt_file is not None:
      assert os.path.exists(
          prompt_file
      ), f"Provided Prompt file does not exist {prompt_file}"
      with open(prompt_file, "r") as f:
          user_prompt = "\n".join(f.readlines())
      evaluate(user_prompt, temperature, top_p, top_k, max_new_tokens)
  elif not sys.stdin.isatty():
      user_prompt = "\n".join(sys.stdin.readlines())
      evaluate(user_prompt, temperature, top_p, top_k, max_new_tokens)
  else:
      gr.Interface(
        fn=evaluate,
        inputs=[
            gr.components.Textbox(
                lines=9,
                label="User Prompt",
                placeholder="none",
            ),
            gr.components.Slider(
                minimum=0, maximum=1, value=1.0, label="Temperature"
            ),
            gr.components.Slider(
                minimum=0, maximum=1, value=1.0, label="Top p"
            ),
            gr.components.Slider(
                minimum=0, maximum=100, step=1, value=50, label="Top k"
            ),
            gr.components.Slider(
                minimum=1, maximum=2000, step=1, value=200, label="Max tokens"
            ),
        ],
        outputs=[
            gr.components.Textbox(
                lines=5,
                label="Output",
            )
        ],
        title="Llama2 Playground",
        description="https://github.com/facebookresearch/llama-recipes",
      ).queue().launch(server_name="0.0.0.0", share=True)

if __name__ == "__main__":
    fire.Fire(main)

보시면 알겠지만, evaluate 함수가 추가되어 있습니다. 지금까지는 CLI만 지원해서 아래와 같이 사용할 수 있었습니다.

python examples/inference.py --model_name '../models/Llama-2-7b-hf' --peft_model 'outputs/7b' --max_new_tokens 580  --quantization true  --prompt_file examples/samsum_prompt.txt

or

cat examples/samsum_prompt.txt |  python examples/inference.py --model_name '../models/Llama-2-7b-hf' --peft_model 'outputs/7b' --max_new_tokens 580  --quantization true

이제 그냥 실행하면,

python examples/inference.py --model_name '../models/Llama-2-7b-hf' --peft_model 'outputs/7b' --max_new_tokens 580  --quantization true
Running on local URL:  http://0.0.0.0:7860
Running on public URL: https://???????????.gradio.live

This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)

재미있는 것은 gradio에서 고유 URL을 제공합니다. 그래서 외부에서 로컬 컴퓨터로 접속 가능하게 해줍니다. examples/samsum_prompt.txt내용을 User Prompt 박스에 입력하고, Submit버튼을 누르면, 아래와 같이 실행이 잘 되는 것을 볼 수 있습니다.

댓글()

Python 간략 정리

기타|2009. 4. 22. 11:26
Python을 배우면서 틈틈히 정리한 내용을 공유합니다. 자주 쓰는 언어가 아니라 그런지 정말 잘 까먹게 되더군요.

그래서 이 문서, 저 코드를 보면서 간략하게 정리했습니다.

Python


1989년 12월 크리스마스를 심심하지 않게 보내려고, 네덜란드 암스테르담에 사는 귀도 반 로섬이 혼자 집에서 취미 삼아 재미로 개발한 프로젝트임. 연구실은 닫히고 집에서 특별히 할일이 없어서 개발했다고 함. 이름은 영국 드라마(Monty Python's Flying Circus)를 참고

http://en.wikipedia.org/wiki/Guido_van_Rossum

Hello World 예제



  1. #!/usr/bin/python
    # hello.py
    print "Hello World\n"


chmod 755 hello.py

Commnet



  1. # 이것은 주석입니다.

  2. import sys   # 이것도 주석입니다.


  3. """

  4. 이것도 주석입니다.

  5. 여러줄을 주석처리할 때 사용합니다...

  6. """


Datatype
































객체 자료형 상수와 사용법의 예
수치형(Numbers) 정수(2134), 실수(2.71), 롱형 정수(345L), 복소수(4+5j)
문자열(Strings) ‘spam’,“ eggs”
리스트(Lists) [1,‘ two’, 3, [5,‘ five’]]
사전(Dictionaries) { 'gslee':5284,'kslee':5582,'mylee':5382}
터플(Tuples) (1,3,’five’)
파일(Files) f = open( ‘toasts’)

정수형 상수

>>> a = 23 # 10진 상수
>>> b = 023 # 8진 상수, 0으로 시작하면 8진수이다.
>>> c = 0x23 # 16진 상수, 0x 혹은 0X로 시작하면 16진수다.
>>> print a, b, c # 10진수로 출력
23 19 35

실수형 상수

이렇게 한 줄에 여러 변수 값을 할당할 수 있다.

>>> e, f, g = 3.14, 2.16e-9, 3E220
>>> print e, f, g
3.14 2.16e-009 3e+220

Long 형 상수

>>> h = 123456789012345678901234567890L
>>> print h * h
15241578753238836750495351562536198787501905199875019052100L

유효 자리수는 메모리가 허용하는 만큼 가능하다.
복소수형 상수

>>> c = 4+5j
>>> d = 7-2J
>>> print c * d
(38+27j)

문자열

문자를 표현하기 위한 자료형

  1. print 'Hello World!'

  2. print "hello World!"


따옴표(Single Quotation mark), 큰 따옴표(Double Quotation Mark) 모두 사용된다.
리스트


  1. L = [1, 4, 3, 2, 5, 'aa']
    print len(L)
    print L[1:3]
    print L[5]
    print L+L
    print L*3

    L.append(6)
    print L
    L.reverse()
    print L
    L.sort()
    print L


list.py
튜플

튜플과 리스트의 차이는 튜플은 값 변경이 안되고, 리스트는 가능하다.

  1. t = (1, 4, 3, 2, 5)

  2. print len(t)

  3. print t[1:3]

  4. print t[1]

  5. print t+t

  6. print t*3


tuple.py
사전 만들기

아이템들은‘키’와‘값(value)’으로‘key:value’형식으로 구성된다.
>>> phone = {} # 공 사전 생성
>>> phone = {'jack':9465215, 'jim':6851325, 'Joseph':6584321} # 초기값 부여
>>> len(phone) # 사전의 크기(아이템의 개수)
3


  1. phone = {}

  2. phone = {'joone':12777347, 'minsoo':23443333, 'Soony':23454458}

  3. print len(phone)

  4. print 'the value of joone is %s' %  phone['joone']


  5. # 검색 추가 변경

  6. phone['jack'] = 33455634

  7. print phone

  8. phone['joone']= 1111111# 삭제

  9. del phone['jack']

  10. print phone# 키, 값, 아이템 얻기

  11. print phone.keys();

  12. print phone.values();

  13. print phone.items();

  14. # 키보드로부터 키 스트링(이름) 입력을 받는다. ‘name?’은 프롬프트다.

  15. name = raw_input('name?')

  16. # 키를 가지고 있는지 검사

  17. if phone.has_key(name):

  18. print phone[name] # 있으면 전화번호 출력

  19. else:

  20. print name,'not found' #없으면


dic.py

관계연산자(relational operator)


> 크다
< 작다
>= 크거나 같다
<= 작거나 같다
== 같다
!= 같지 않다
<> 같지 않다

포맷 스트링



  1. s3 = 'Hi %s! How are you doing?'

  2. print s3 % "joone"

  3. s4 = '%s * %s = %s'

  4. print s4 % (12, 23, 12*23)

  5. print '%5d %o %x %5.2f' % (23, 23, 23, 43.123)


제어문


파이썬은 단순하게 if, for, while이라는 3개의 제어문을 갖고 있다.
if 문


  1. n = int(raw_input('value?'))

  2. if n > 0:

  3. print 'positive'

  4. elif n < 0:

  5. print 'negative'

  6. else:

  7. print 'zero'


한 가지 주의 깊게 봐야 할 점이 있는데 들여쓰기(indentation)이다. 즉, if - elif - else는 같은 열에 있어야 하고, 세 개의 print 문은 탭
혹은 같은 수의 스페이스를 이용해 들여쓰기를 한다. 파이썬에서는 이와 같은 들여쓰기 필수다.
for 문


  1. a = [0,1,2,3]
    for x in a:      # a에 있는 모든 x에 대해
    print x,    # x값 출력



  1. for x in range(100):

  2. print x


순차적으로 숫자를 증가시켜가면서 반복할 경우에는 range함수를 이용한다
while문


  1. a, b = 0, 1
    while b < 100:
    print b,
    a, b = b, a+b


함수 정의와 호출


def 함수명 (인수들..):
문(statements)
return <값>


  1. def add(a, b):

  2. return a+b

  3. print add(1,2)



  1. add(int, int);
    add(int, double);
    add(double, int);
    add(double, double);


파이썬은 자료형을 동적으로 결정하므로 다른 언어에서 해야 하는 많은 중복(overloading) 작업을 피하게 해준다.

파일 입출력



  • 파이썬의 파일 입출력(File I/O) 기능은 C에서 사용하는 함수를 확장한 것

  • C의 래퍼(wrapper)로서 대부분의 C 파일 입출력 기능과 추가적인 편의 함수를 제공

  • 파일을 사용하기에 앞서 우선 파일을 오픈해야 하는데,

  • 이때 파일명과 처리 모드를 인수로 넘겨줘야 한다. 처리 모드는‘w:쓰기, r:읽기, a:추가하기’이다.



  1. f = open(‘testfile’, ‘w’)                  # 쓰기 모드로 testfile을 오픈한다.
    f.write(‘파이썬 파일 쓰기 테스트 중이예요!\n’)    # 문자열을 출력한다
    f.close()                                  # 파일을 닫는다. 생략 가능


읽기 쓰기 메쏘드
파일 객체의 . 더 다양한 메쏘드에 대해 배워보자. 먼저 읽기를 위한 메쏘드는

read()     파일 전체 내용을 한꺼번에 문자열로 읽어들인다

read(N)   N의 양의 정수만큼 읽기

readline()  한줄씩 읽기

readlines() 파일 전체를 라인단위로 끊어서 리스트에 저장한다
write(S), writelines(L)

파일 목록 읽기



  1. #!/usr/bin/python
    # file : readfiles.py

    import sys
    import os

    def main(argv):
    fileNames = os.listdir(argv[0])
    fileNames.sort()

    for fileName in fileNames:
    print '%s/%s' % (argv[0], fileName)

    if __name__=="__main__":
    main(sys.argv[1:])


명령행 인수 처리



  1. import sys # for handling argv
    import re   # import regular expression module for using subn

    def replace(fname, srcstr, deststr):
    f = open(fname)
    txt = f.read()
    txt = re.subn(srcstr, deststr, txt)[0]
    return txt

    if __name__ == '__main__':
    if len(sys.argv) != 4:
    print "Usage : replace filename srcstr deststr"
    sys.exit()
    print replace(sys.argv[1], sys.argv[2], sys.argv[3])


command.py

  1. import sys
    import os

  2. def main(argv):
    for arg in argv:
    print arg
    if __name__=="__main__":
    main(sys.argv[1:])


OOP



  1. class MyClass:

  2. def setValue(self, val):

  3. self.value = val;

  4. def getValue(self):

  5. print self.value

  6. c = MyClass()

  7. c.setValue('Hello')

  8. c.getValue()


oop.py

Debugging


python -m pdb oop.py

참고문헌



  • 이강성, 파이썬의 기본 구문과 기초자료형 이해, 마이크로소프트웨어 2000년 7월호


This article was written in springnote.

'기타' 카테고리의 다른 글

지키드리지 못해 죄송합니다...  (0) 2009.05.24
About  (0) 2009.05.22
2009년을 맞이하며 - 참여하는 한 해가 되자!!  (4) 2009.01.05
오바마 당선을 축하하며~  (0) 2008.11.05
HDTV에서 인터넷을?  (0) 2008.03.30

댓글()