6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HuggingFaceのLLMのメモリ使用率を調べる

Posted at

とあるプロジェクトでLLMを触っているが、LLMをはじめ言語のNNやHuggingFaceを触るのは初めてなので、そもそものリソース使用率を調べる

対象は日本語が使える rinna と open-calm

設定

環境はこんなところ

  • GCE
  • イメージ
    • c0-deeplearning-common-cu113-v20230412-debian-10
  • n1-highmem-8
    • 8コア
    • 52GBメモリ
    • 200GBストレージ
  • NVIDIA T4
    • 16GBメモリ
FROM python:3.10-slim

WORKDIR /app

COPY . .

RUN pip install \
    torch \
    transformers \
    sentencepiece \
    memory-profiler \
    accelerate \
    ;
docker-compose.yaml
services:
  app:
    image: foo
    build:
      context: ./app
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 48G
        reservations:
          cpus: '2.0'
          memory: 20G
    volumes:
      - ./app:/app
    command: [ "python", "main.py", "***" ]  # 使うモデルを入れる

実行時引数でモデルを与えてメモリ使用率を見る

ややこしい文章課題は行わない
(長文の要約など)

rinna

main.py
import sys
import time
from logging import getLogger

import torch
from memory_profiler import profile
from transformers import AutoTokenizer, AutoModelForCausalLM

logger = getLogger(__name__)

dict_model = {
    "rinna": "rinna/japanese-gpt-neox-3.6b-instruction-sft-v2",
    "calm": "cyberagent/open-calm-1b",
}
prompter = {
    "rinna": lambda x: f"ユーザー: {x}<NL>システム: ",
    "calm": lambda x: x + ""
}

fout = open("musage.txt", "w")

@profile(stream=fout)
def main():
    start = time.time()
    logger.warning(f"start: {start - start}")

    try:
        framework = dict_model[sys.argv[1]]
    except Exception:
        logger.error("give an arg of rinna or calm")
        raise

    tokenizer = AutoTokenizer.from_pretrained(framework, use_fast=False)
    now = time.time()
    logger.warning(f"tokenizer loaded: {now - start}")

    model = AutoModelForCausalLM.from_pretrained(framework)
    now = time.time()
    logger.warning(f"model loaded: {now - start}")

    if torch.cuda.is_available():
        model = model.to("cuda")

    prompt = "フリーキャッシュフローとは何ですか"
    prompt = prompter[sys.argv[1]](prompt)
    logger.warning(f"prompt: {prompt}")

    token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
    now = time.time()
    logger.warning(f"prompt encoded: {now - start}")

    with torch.no_grad():
        output_ids = model.generate(
            token_ids.to(model.device),
            do_sample=True,
            max_new_tokens=128,
            temperature=0.7,
            repetition_penalty=1.1,
            pad_token_id=tokenizer.pad_token_id,
            bos_token_id=tokenizer.bos_token_id,
            eos_token_id=tokenizer.eos_token_id
        )

    now = time.time()
    logger.warning(f"response generated: {now - start}")

    output = tokenizer.decode(output_ids.tolist()[0][token_ids.size(1):])
    now = time.time()
    logger.warning(f"token decoded: {now - start}")

    output = output.replace("<NL>", "\n")
    print(output)


if __name__ == "__main__":
    main()

モデルの取得に一番時間がかかる
回答の生成にも時間がかかる
cpu だけでも動く

temp-app-1  | start: 0.0
temp-app-1  | tokenizer loaded: 0.5645828247070312
temp-app-1  | model loaded: 35.02510094642639
temp-app-1  | prompt: ユーザー: フリーキャッシュフローとは何ですか<NL>システム:
temp-app-1  | prompt encoded: 35.029651165008545
temp-app-1  | response generated: 55.829381704330444
temp-app-1  | token decoded: 55.838337659835815
temp-app-1  | フリーキャッシュフローとは、現金の純増分のことです。企業が製品やサービスを販売し、顧客との取引から得た支払いを使っていくことで、お金が増えます。財務的な観点からは、こ
れは「良い」と言えます。</s>

モデルのロードで14GB使っているが、実際には20GBは必要
ちなみに sft-v2 は事前学習済みのモデルを対話用にファインチューニングしたもの (なはず) で、
rinna の中では重い方

Filename: /app/main.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    22    318.5 MiB    318.5 MiB           1   @profile(stream=fout)
    23                                         def main():
    24    318.5 MiB      0.0 MiB           1       start = time.time()
    25    318.5 MiB      0.0 MiB           1       logger.warning(f"start: {start - start}")
    26                                         
    27    318.5 MiB      0.0 MiB           1       try:
    28    318.5 MiB      0.0 MiB           1           framework = dict_model[sys.argv[1]]
    29                                             except Exception:
    30                                                 logger.error("give an arg of rinna or calm")
    31                                                 raise
    32                                         
    33    332.4 MiB     13.8 MiB           1       tokenizer = AutoTokenizer.from_pretrained(framework, use_fast=False)
    34    332.4 MiB      0.0 MiB           1       now = time.time()
    35    332.4 MiB      0.0 MiB           1       logger.warning(f"tokenizer loaded: {now - start}")
    36                                         
    37  14387.8 MiB  14055.5 MiB           1       model = AutoModelForCausalLM.from_pretrained(framework)
    38  14387.8 MiB      0.0 MiB           1       now = time.time()
    39  14387.8 MiB      0.0 MiB           1       logger.warning(f"model loaded: {now - start}")
    40                                         
    41  14388.0 MiB      0.1 MiB           1       if torch.cuda.is_available():
    42                                                 model = model.to("cuda")
    43                                         
    44  14388.0 MiB      0.0 MiB           1       prompt = "フリーキャッシュフローとは何ですか"
    45  14388.0 MiB      0.0 MiB           1       prompt = prompter[sys.argv[1]](prompt)
    46  14388.0 MiB      0.0 MiB           1       logger.warning(f"prompt: {prompt}")
    47                                         
    48  14388.0 MiB      0.0 MiB           1       token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
    49  14388.0 MiB      0.0 MiB           1       now = time.time()
    50  14388.0 MiB      0.0 MiB           1       logger.warning(f"prompt encoded: {now - start}")
    51                                         
    52  14498.7 MiB      0.0 MiB           2       with torch.no_grad():
    53  14498.7 MiB    110.8 MiB           2           output_ids = model.generate(
    54  14388.0 MiB      0.0 MiB           1               token_ids.to(model.device),
    55  14388.0 MiB      0.0 MiB           1               do_sample=True,
    56  14388.0 MiB      0.0 MiB           1               max_new_tokens=128,
    57  14388.0 MiB      0.0 MiB           1               temperature=0.7,
    58  14388.0 MiB      0.0 MiB           1               repetition_penalty=1.1,
    59  14388.0 MiB      0.0 MiB           1               pad_token_id=tokenizer.pad_token_id,
    60  14388.0 MiB      0.0 MiB           1               bos_token_id=tokenizer.bos_token_id,
    61  14388.0 MiB      0.0 MiB           1               eos_token_id=tokenizer.eos_token_id
    62                                                 )
    63                                         
    64  14498.7 MiB      0.0 MiB           1       now = time.time()
    65  14498.7 MiB      0.0 MiB           1       logger.warning(f"response generated: {now - start}")
    66                                         
    67  14498.7 MiB      0.0 MiB           1       output = tokenizer.decode(output_ids.tolist()[0][token_ids.size(1):])
    68  14498.7 MiB      0.0 MiB           1       now = time.time()
    69  14498.7 MiB      0.0 MiB           1       logger.warning(f"token decoded: {now - start}")
    70                                         
    71  14498.7 MiB      0.0 MiB           1       output = output.replace("<NL>", "\n")
    72  14498.7 MiB      0.0 MiB           1       print(output)

Open-CALM

1B で CALM の中では中くらいの重さ

rinna から細かいパラメータを変えた

main.py
import sys
import time
from logging import getLogger

import torch
from memory_profiler import profile
from transformers import AutoTokenizer, AutoModelForCausalLM

logger = getLogger(__name__)

dict_model = {
    "rinna": "rinna/japanese-gpt-neox-3.6b-instruction-sft-v2",
    "calm": "cyberagent/open-calm-1b",
}
prompter = {
    "rinna": lambda x: f"ユーザー: {x}<NL>システム: ",
    "calm": lambda x: x,
}

fout = open("musage.txt", "w")

@profile(stream=fout)
def main():
    start = time.time()
    logger.warning(f"start: {start - start}")

    try:
        framework = dict_model[sys.argv[1]]
    except Exception:
        logger.error("give an arg of rinna or calm")
        raise

    tokenizer = AutoTokenizer.from_pretrained(framework)
    now = time.time()
    logger.warning(f"tokenizer loaded: {now - start}")

    model = AutoModelForCausalLM.from_pretrained(
        framework, device_map="auto", torch_dtype=torch.float32,
    )
    now = time.time()
    logger.warning(f"model loaded: {now - start}")

    if torch.cuda.is_available():
        model = model.to("cuda")

    prompt = "フリーキャッシュフローとは何ですか"
    prompt = prompter[sys.argv[1]](prompt)
    logger.warning(f"prompt: {prompt}")

    token_ids = tokenizer.encode(prompt, return_tensors="pt")
    now = time.time()
    logger.warning(f"prompt encoded: {now - start}")

    with torch.no_grad():
        output_ids = model.generate(
            token_ids.to(model.device),
            do_sample=True,
            max_new_tokens=128,
            temperature=0.7,
            top_p=0.9,
            repetition_penalty=1.1,
            pad_token_id=tokenizer.pad_token_id,
            #bos_token_id=tokenizer.bos_token_id,
            #eos_token_id=tokenizer.eos_token_id
        )

    now = time.time()
    logger.warning(f"response generated: {now - start}")

    output = tokenizer.decode(output_ids[0], skip_special_tokens=True)
    now = time.time()
    logger.warning(f"token decoded: {now - start}")

    output = output.replace("<NL>", "\n")
    print(output)


if __name__ == "__main__":
    main()

huggingfaceのドキュメント通りに利用しようとするとエラーが出る
RuntimeError: "LayerNormKernelImpl" not implemented for 'Half'
https://github.com/huggingface/transformers/issues/21989#issuecomment-1461999591
float16を使うにはGPUが必要らしい
なので torch_ftype=torch.float32 を使った

とりあえず CPU だけでも動く

モデルのロードは比較的早い
それと比較すると回答の生成に時間がかかる上に、回答が意図したものではない

temp-app-1  | start: 0.0
temp-app-1  | tokenizer loaded: 0.3371620178222656
temp-app-1  | The model weights are not tied. Please use the `tie_weights` method before using the `infer_auto_device` function.
temp-app-1  | model loaded: 8.491863489151001
temp-app-1  | prompt: フリーキャッシュフローとは何ですか
temp-app-1  | prompt encoded: 8.495642185211182
temp-app-1  | response generated: 36.54288625717163
temp-app-1  | token decoded: 36.544095516204834
temp-app-1  | フリーキャッシュフローとは何ですか。
temp-app-1  | 【財務】負債比率とはどういう意味でしょうか?また、その計算方法を教えてください。【ESG/CSR】【労務管理・衛生要因分析(HACCP)】「経営戦略」と「組織運営方針」「人事制度等 の基本的仕組み」、これら3つについて教えてください。(製造業)【経理業務効率化支援ツール】(経費精算システム)「勤怠管理システム」、「会計帳簿作成ソフト」)のそれぞれの機能概要はどのようなものなのでしょうか?【総務系サービス開発プロジェクト事例紹介】・社内イントラネットに社員ブログを設置したい。・社内で使用するPCが老朽化したため新しく買い替えたい

モデルサイズも rinna よりは小さいのか、メモリを6GBほどしか使わない

Filename: /app/main.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    22    314.6 MiB    314.6 MiB           1   @profile(stream=fout)
    23                                         def main():
    24    314.6 MiB      0.0 MiB           1       start = time.time()
    25    314.6 MiB      0.0 MiB           1       logger.warning(f"start: {start - start}")
    26                                         
    27    314.6 MiB      0.0 MiB           1       try:
    28    314.6 MiB      0.0 MiB           1           framework = dict_model[sys.argv[1]]
    29                                             except Exception:
    30                                                 logger.error("give an arg of rinna or calm")
    31                                                 raise
    32                                         
    33    332.8 MiB     18.2 MiB           1       tokenizer = AutoTokenizer.from_pretrained(framework)
    34    332.8 MiB      0.0 MiB           1       now = time.time()
    35    332.8 MiB      0.0 MiB           1       logger.warning(f"tokenizer loaded: {now - start}")
    36                                         
    37   6930.0 MiB   6597.2 MiB           2       model = AutoModelForCausalLM.from_pretrained(
    38    332.8 MiB      0.0 MiB           1           framework, device_map="auto", torch_dtype=torch.float32,
    39                                             )
    40   6930.0 MiB      0.0 MiB           1       now = time.time()
    41   6930.0 MiB      0.0 MiB           1       logger.warning(f"model loaded: {now - start}")
    42                                         
    43   6930.0 MiB      0.0 MiB           1       if torch.cuda.is_available():
    44                                                 model = model.to("cuda")
    45                                         
    46   6930.0 MiB      0.0 MiB           1       prompt = "フリーキャッシュフローとは何ですか"
    47   6930.0 MiB      0.0 MiB           1       prompt = prompter[sys.argv[1]](prompt)
    48   6930.0 MiB      0.0 MiB           1       logger.warning(f"prompt: {prompt}")
    49                                         
    50   6930.0 MiB      0.0 MiB           1       token_ids = tokenizer.encode(prompt, return_tensors="pt")
    51   6930.0 MiB      0.0 MiB           1       now = time.time()
    52   6930.0 MiB      0.0 MiB           1       logger.warning(f"prompt encoded: {now - start}")
    53                                         
    54   6957.4 MiB      0.0 MiB           2       with torch.no_grad():
    55   6957.4 MiB     27.4 MiB           2           output_ids = model.generate(
    56   6930.0 MiB      0.0 MiB           1               token_ids.to(model.device),
    57   6930.0 MiB      0.0 MiB           1               do_sample=True,
    58   6930.0 MiB      0.0 MiB           1               max_new_tokens=128,
    59   6930.0 MiB      0.0 MiB           1               temperature=0.7,
    60   6930.0 MiB      0.0 MiB           1               top_p=0.9,
    61   6930.0 MiB      0.0 MiB           1               repetition_penalty=1.1,
    62   6930.0 MiB      0.0 MiB           1               pad_token_id=tokenizer.pad_token_id,
    63                                                     #bos_token_id=tokenizer.bos_token_id,
    64                                                     #eos_token_id=tokenizer.eos_token_id
    65                                                 )
    66                                         
    67   6957.4 MiB      0.0 MiB           1       now = time.time()
    68   6957.4 MiB      0.0 MiB           1       logger.warning(f"response generated: {now - start}")
    69                                         
    70   6957.4 MiB      0.0 MiB           1       output = tokenizer.decode(output_ids[0], skip_special_tokens=True)
    71   6957.4 MiB      0.0 MiB           1       now = time.time()
    72   6957.4 MiB      0.0 MiB           1       logger.warning(f"token decoded: {now - start}")
    73                                         
    74   6957.4 MiB      0.0 MiB           1       output = output.replace("<NL>", "\n")
    75   6957.4 MiB      0.0 MiB           1       print(output)

終わりに

正直、CPUだけでも動くか、メモリ使用率の目安を知りたかった
推論のためだけにGPUを使うのはコストなので、CPUだけでも動くようでよかった
まぁ、大きなモデルを高速に動かそうと思ったらGPUいるかもだが

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?