見出し画像

Scikit-LLM をシュッと試してみた

Twitter を眺めていたら Scikit-LLM というのを目にしたのでシュッと試してみました。

と、その前に。。。

名前が何かに似ていると思いませんか?

そう!

機械学習でおなじみ(らしい)scikit-learn ですね!!
(私はほぼ使ったことないです。はい。すいません。)

scikit-learnは、Pythonで利用できるオープンソースの機械学習ライブラリです。さまざまな機械学習アルゴリズムを提供し、簡単なインターフェースと豊富な機能を備えています。データの前処理や特徴エンジニアリング、モデルの評価などもサポートしています。また、充実したドキュメントとコミュニティもあります。scikit-learnは、機械学習の初心者からプロまで広く利用されています。

Generated by ChatGPT


はい、では前置きはこれくらいにしてみていきましょうかね

What is Scikit-LLM ?

何ができますのん?( ゚д゚)

ということでシュッと README を基に ChatGPT にまとめてもらいました。

Scikit-LLM は、テキスト分析をパワーアップするためのツールだよ。具体的には、以下のようなことができるんだ。

ゼロショットテキスト分類: ラベルが説明的であれば、再学習せずにテキスト分類ができるんだ。これは ZeroShotGPTClassifie rを使うことで可能になるよ。

マルチラベルゼロショットテキスト分類: 各サンプルが複数のクラスに割り当てられる場合でも、MultiLabelZeroShotGPTClassifier を使えば分類ができるよ。

フューショットテキスト分類: FewShotGPTClassifier を使えば、トレーニングサンプルをプロンプトに追加してモデルに渡すことで、フューショット分類ができるよ。

テキストベクトル化: GPTVectorizer を使えば、任意の長さのテキストを固定次元のベクトルに変換できるよ。これは、ほとんどの分類モデルや回帰モデルと一緒に使えるんだ。

テキストの要約: GPTSummarizer を使えば、テキストを要約することができるよ。これは、単独で使うことも、前処理として使うこともできるよ。

テキスト翻訳: GPTTranslatorを使えば、任意のテキストを目的の言語に翻訳することができるよ。

これらの機能を使うためには、OpenAI の API キーを設定する必要があるよ。そして、Scikit-LLM は OpenAI のモデルとのみ互換性があるから、その点は注意が必要だよ。

Created by ChatGPT

ほーん?( ゚д゚)

まぁとりあえず使ってみましょ。

Let's do it

シュッと Google Colab で動作を確認していきますよん。

!pip install git+https://github.com/iryna-kondr/scikit-llm.git 

↑ 検証時点(2023/05/30)ではこうやらないと FewShotGPTClassifier が使えないのであった!

からの OpenAI の認証情報をセット!

from skllm.config import SKLLMConfig

SKLLMConfig.set_openai_key("")
SKLLMConfig.set_openai_org("")

Organization ID が何故必要なのかは謎。。。
(そして現状 OpenAI しか対応してない)

でまぁとりあえずチュートリアルを触りながら動作を確認

Zero-Shot

from skllm import ZeroShotGPTClassifier
from skllm.datasets import get_classification_dataset

# demo sentiment analysis dataset
# labels: positive, negative, neutral
X, y = get_classification_dataset()

clf = ZeroShotGPTClassifier(openai_model="gpt-3.5-turbo")
clf.fit(X, y)
labels = clf.predict(X)

データセットはこんな感じ(GitHub でみれるよ)

def get_classification_dataset():
    X = [
        r"I was absolutely blown away by the performances in 'Summer's End'. The acting was top-notch, and the plot had me gripped from start to finish. A truly captivating cinematic experience that I would highly recommend.",
        r"The special effects in 'Star Battles: Nebula Conflict' were out of this world. I felt like I was actually in space. The storyline was incredibly engaging and left me wanting more. Excellent film.",
        r"'The Lost Symphony' was a masterclass in character development and storytelling. The score was hauntingly beautiful and complimented the intense, emotional scenes perfectly. Kudos to the director and cast for creating such a masterpiece.",
        r"I was pleasantly surprised by 'Love in the Time of Cholera'. The romantic storyline was heartwarming and the characters were incredibly realistic. The cinematography was also top-notch. A must-watch for all romance lovers.",
        r"I went into 'Marble Street' with low expectations, but I was pleasantly surprised. The suspense was well-maintained throughout, and the twist at the end was something I did not see coming. Bravo!",
        r"'The Great Plains' is a touching portrayal of life in rural America. The performances were heartfelt and the scenery was breathtaking. I was moved to tears by the end. It's a story that will stay with me for a long time.",
        r"The screenwriting in 'Under the Willow Tree' was superb. The dialogue felt real and the characters were well-rounded. The performances were also fantastic. I haven't enjoyed a movie this much in a while.",
        r"'Nightshade' is a brilliant take on the superhero genre. The protagonist was relatable and the villain was genuinely scary. The action sequences were thrilling and the storyline was engaging. I can't wait for the sequel.",
        r"The cinematography in 'Awakening' was nothing short of spectacular. The visuals alone are worth the ticket price. The storyline was unique and the performances were solid. An overall fantastic film.",
        r"'Eternal Embers' was a cinematic delight. The storytelling was original and the performances were exceptional. The director's vision was truly brought to life on the big screen. A must-see for all movie lovers.",
        r"I was thoroughly disappointed with 'Silver Shadows'. The plot was confusing and the performances were lackluster. I wouldn't recommend wasting your time on this one.",
        r"'The Darkened Path' was a disaster. The storyline was unoriginal, the acting was wooden and the special effects were laughably bad. Save your money and skip this one.",
        r"I had high hopes for 'The Final Frontier', but it failed to deliver. The plot was full of holes and the characters were poorly developed. It was a disappointing experience.",
        r"'The Fall of the Phoenix' was a letdown. The storyline was confusing and the characters were one-dimensional. I found myself checking my watch multiple times throughout the movie.",
        r"I regret wasting my time on 'Emerald City'. The plot was nonsensical and the performances were uninspired. It was a major disappointment.",
        r"I found 'Hollow Echoes' to be a complete mess. The plot was non-existent, the performances were overdone, and the pacing was all over the place. Definitely not worth the hype.",
        r"'Underneath the Stars' was a huge disappointment. The storyline was predictable and the acting was mediocre at best. I was expecting so much more.",
        r"I was left unimpressed by 'River's Edge'. The plot was convoluted, the characters were uninteresting, and the ending was unsatisfying. It's a pass for me.",
        r"The acting in 'Desert Mirage' was subpar, and the plot was boring. I found myself yawning multiple times throughout the movie. Save your time and skip this one.",
        r"'Crimson Dawn' was a major letdown. The plot was cliched and the characters were flat. The special effects were also poorly executed. I wouldn't recommend it.",
        r"'Remember the Days' was utterly forgettable. The storyline was dull, the performances were bland, and the dialogue was cringeworthy. A big disappointment.",
        r"'The Last Frontier' was simply okay. The plot was decent and the performances were acceptable. However, it lacked a certain spark to make it truly memorable.",
        r"'Through the Storm' was not bad, but it wasn't great either. The storyline was somewhat predictable, and the characters were somewhat stereotypical. It was an average movie at best.",
        r"I found 'After the Rain' to be pretty average. The plot was okay and the performances were decent, but it didn't leave a lasting impression on me.",
        r"'Beyond the Horizon' was neither good nor bad. The plot was interesting enough, but the characters were not very well developed. It was an okay watch.",
        r"'The Silent Echo' was a mediocre movie. The storyline was passable and the performances were fair, but it didn't stand out in any way.",
        r"I thought 'The Scent of Roses' was pretty average. The plot was somewhat engaging, and the performances were okay, but it didn't live up to my expectations.",
        r"'Under the Same Sky' was an okay movie. The plot was decent, and the performances were fine, but it lacked depth and originality. It's not a movie I would watch again.",
        r"'Chasing Shadows' was fairly average. The plot was not bad, and the performances were passable, but it lacked a certain spark. It was just okay.",
        r"'Beneath the Surface' was pretty run-of-the-mill. The plot was decent, the performances were okay, but it wasn't particularly memorable. It was an okay movie.",
    ]


    y = (
        ["positive" for _ in range(10)]
        + ["negative" for _ in range(10)]
        + ["neutral" for _ in range(10)]
    )

    return X, y

結果がこちら

[
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral'
]

結局のところデータセットとしては X を OpenAI API に投げてその結果を定義したラベルで分類すると。

今回の結果だと提供されていたデータセットの y が
posive: 10
negative:10
neutral: 10

だったのに、OpenAI の分類を通すと
positive: 10
negative: 11
neutral: 9

という風に変化したことはわかるでござんす。

んー?使い道が良くわからんが自分の定義したラベルがあっているかどうか LLM に確認してもらおうということなのかしら?( ゚д゚)


まぁそれはさておきチュートリアルを進めましょう


Multi-Label 

from skllm import MultiLabelZeroShotGPTClassifier
from skllm.datasets import get_multilabel_classification_dataset

X, y = get_multilabel_classification_dataset()

clf = MultiLabelZeroShotGPTClassifier(max_labels=3)
clf.fit(X, y)
labels = clf.predict(X)

今回のデータはこちら

def get_multilabel_classification_dataset():
    X = [
    "The product was of excellent quality, and the packaging was also very good. Highly recommend!",
    "The delivery was super fast, but the product did not match the information provided on the website.",
    "Great variety of products, but the customer support was quite unresponsive.",
    "Affordable prices and an easy-to-use website. A great shopping experience overall.",
    "The delivery was delayed, and the packaging was damaged. Not a good experience.",
    "Excellent customer support, but the return policy is quite complicated.",
    "The product was not as described. However, the return process was easy and quick.",
    "Great service and fast delivery. The product was also of high quality.",
    "The prices are a bit high. However, the product quality and user experience are worth it.",
    "The website provides detailed information about products. The delivery was also very fast."
    ]

    y = [
        ["Quality", "Packaging"],
        ["Delivery", "Product Information"],
        ["Product Variety", "Customer Support"],
        ["Price", "User Experience"],
        ["Delivery", "Packaging"],
        ["Customer Support", "Return Policy"],
        ["Product Information", "Return Policy"],
        ["Service", "Delivery", "Quality"],
        ["Price", "Quality", "User Experience"],
        ["Product Information", "Delivery"],
    ]

    return X, y

結果

[
  ['Quality', 'Packaging'],
  ['Delivery', 'Product Information'],
  ['Product Variety', 'Customer Support'],
  ['Price', 'User Experience'],
  ['Delivery', 'Packaging'],
  ['Customer Support', 'Return Policy'],
  ['Product Information', 'Return Policy'],
  ['Delivery', 'Quality', 'Service'],
  ['Price', 'Quality', 'User Experience'],
  ['Product Information', 'Delivery']
]

複数の分類が可能になってるね!!
でもデータセットのラベルと結果が変わらないから反応に困るね!!


Few-Shot

from skllm import FewShotGPTClassifier
from skllm.datasets import get_classification_dataset

X, y = get_classification_dataset()

clf = FewShotGPTClassifier(openai_model="gpt-3.5-turbo")
clf.fit(X, y)
labels = clf.predict(X)

データセットは最初の Zero-Shot の時と同じでございます。

で、結果はこちら

[
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'positive',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'negative',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral',
  'neutral'
]

特に Zero-Shot と結果は変化なし ( ゚д゚)

ていうか Few-Shot ってことは自分でプロンプトに何か追記できるんか?
でもそんなオプションないしな?

そう思った私はソースコードを覗きに行くのであった。。。

結論。

現時点ではデータセットをそのまま評価用のプロンプトにぶち込んで無理やり Few-Shot にしてたぜ (*´▽`*)

つまり、巨大なデータセットを使ってはいけません。。。

ぐぬぬぬぬぬ。


まぁ出来立てほやほやの機能だからまぁいいでしょう。

ベクトル化

from skllm.preprocessing import GPTVectorizer

model = GPTVectorizer()
vectors = model.fit_transform(X)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import LabelEncoder
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

le = LabelEncoder()
y_train_encoded = le.fit_transform(y_train)
y_test_encoded = le.transform(y_test)

steps = [("GPT", GPTVectorizer()), ("Clf", XGBClassifier())]
clf = Pipeline(steps)
clf.fit(X_train, y_train_encoded)
yh = clf.predict(X_test)

これはまぁデータセットを GPT でベクトル化して XGBClassifier みたいな定番のやつで分類しやすくしたよ!

っていう機能ですな。

結果はこちら

[1 1 2 1 2 2]

うん。いわゆる分類をしている感じだね。

要約

from skllm.preprocessing import GPTSummarizer
from skllm.datasets import get_summarization_dataset

X = get_summarization_dataset()
s = GPTSummarizer(openai_model="gpt-3.5-turbo", max_words=15)
summaries = s.fit_transform(X)

これはまぁ、要するにあれですな。
要約です。

普通に ChatGPT とかでできるけどデータセットを渡すだけで簡単にやりまっせ~

っていう感じかな。

まぁ予想通り単純に要約するだけの 結果に。

[
 'OpenAI launches GPT-4, a powerful language model for complex tasks.'
 'John bought groceries and made a fruit salad for a get-together with friends.'
 "NASA's 1996 Mars Pathfinder project successfully launched the first Mars rover, Sojourner."
 'Regular exercise improves memory and cognitive function in older adults, recommends 30 minutes daily.'
 'Eiffel Tower, Paris landmark completed in 1889, symbolizes French architectural innovation.'
 'Microsoft announces new Windows version with improved security and redesigned interface.'
 'WHO declares new public health emergency, urges nations to improve disease surveillance and response.'
 'Paris to host 2024 Olympics, third time for the city.'
 'Apple unveils new iPhone with improved camera, faster processor, and longer battery life.'
 'New bird species found in Amazon rainforest with unique bright plumage, exciting ornithological community.'
]

翻訳

これもまぁ要約と同じくデータ渡せばシュッとやりまっせ~系の機能ですな。

from skllm.preprocessing import GPTTranslator
from skllm.datasets import get_translation_dataset

X = get_translation_dataset()
t = GPTTranslator(openai_model="gpt-3.5-turbo", output_language="Japanese")
translated_text = t.fit_transform(X)

結果はこちら

[
 '私はサルサとバチャータを踊ることが大好きです。それは私が自己表現する楽しい方法です。'
 '私は最近の休暇をギリシャで過ごしました。ビーチは美しかったです。' '私は昨日素晴らしい本を読みました。物語は最後まで魅力的でした。'
 '私は伝統的なイタリア料理を作ることが好きです。 カルボナーラスパゲッティは私のお気に入りの一つです。'
 '計画しているのは、今年の夏にイタリア旅行に出かけることです。ローマとヴェネツィアを訪れることを願っています。'
 '私のお気に入りの趣味は写真を撮ることです。美しい瞬間を捉えることが大好きです。'
]

うん。そうだよね。



チャックノリスファクト


ということでさらっと機能を見たのであるが

しかしこれだけで終わるとあれなので最後にチャックノリスファクトについて分類してみてもらった。

まずはチャック・ノリスのデータをシュッと定義

def get_chuck_norris_dataset():
    X = [
        r"チャック・ノリスは、一度に二度同じ場所に存在することができます。",
        r"チャック・ノリスが涙を流すと、その涙は癌を治すことができます。問題は、チャック・ノリスが泣かないということです。",
        r"チャック・ノリスは歯医者に行くことはありません。彼の歯は誰も引き抜く勇気がありません。",
        r"チャック・ノリスはかけっこで二回目の一着を取りました。",
        r"チャック・ノリスがプールで水泳をするとき、彼が動くのではなく、水が避けます。",
        r"チャック・ノリスは壁に向かって行きます。彼が通るために、壁はどかなければなりません。",
        r"チャック・ノリスは、早すぎて過去にタイムトラベルできます。",
        r"チャック・ノリスは一度に両方の方向に回転することができます。",
        r"チャック・ノリスは夜に眠りません。彼は待つだけです。",
        r"チャック・ノリスは太陽を汗で冷やすことができます。",
        r"チャック・ノリスが月を蹴った結果、その跡が今も残っています。それがクレーターです。",
        r"チャック・ノリスは眠ることはありません。彼は休息を取ります。",
        r"チャック・ノリスはカップラーメンを食べるのに2分も待ちません。",
        r"チャック・ノリスは海に向かって浜辺を引き寄せます。",
        r"チャック・ノリスは火を見て恐怖を感じさせることができます。",
        r"チャック・ノリスは玉ねぎを泣かせる",
        r"チャックノリスのピースサインは「あと二秒で殺す」の意味",
        r"日本にはゴジラがいるが、テキサスにはチャック・ノリスがいる",
        r"チャック・ノリスは呼吸するのではない。空気を人質に取るのだ",
        r"生まれた赤ちゃんが泣くのは、この世にチャック・ノリスが居ることを知っているから",
        r"チャック・ノリスの動くスピードは二種類。1.歩く 2.殺す",
        r"チャック・ノリスは、Twitterを必要としない。彼はすでにあなたをフォローしている",
        r"合衆国には4つの法的な処刑方法がある。 致死量の注射、ガス室、電気椅子、チャック・ノリス",
        r"アメリカ合衆国の主な死因は 1.心臓病 2.チャック・ノリス 3.癌 である",
        r"一部の人々は国家の法律を破るが、チャック・ノリスは物理学の法則を破る",
        r"地球は太陽の周りを公転しているが、宇宙はチャック・ノリスを中心に回っている",
        r"チャック・ノリスはサンタを信じていないがサンタはチャック・ノリスを信じている",
        r"チャック・ノリスは夢を見ることがあります。一度だけですが、その日以来、その夢は現実となっています。",
        r"チャック・ノリスが部屋に入ると、床が振るえます。それは恐怖でなく、敬意からです。",
        r"チャック・ノリスは自分の名前を言うだけで、ドアを開けることができます。",
    ]

    return X

それを possible or impossible で評価。
その際事前にラベルはつけずに Zero-Shot で確認する。

from skllm import ZeroShotGPTClassifier

X = get_chuck_norris_dataset()

clf = ZeroShotGPTClassifier()
clf.fit(None, ["possible", "impossible"])
labels = clf.predict(X)

結果は、、、









ドーーーーーン!!

[
  'impossible',
  'impossible',
  'impossible',
  'impossible',
  'impossible',
  'impossible',
  'impossible',
  'possible',
  'possible',
  'impossible',
  'impossible',
  'possible',
  'impossible',
  'impossible',
  'impossible',
  'impossible',
  'possible',
  'impossible',
  'impossible',
  'impossible',
  'possible',
  'impossible',
  'impossible',
  'possible',
  'impossible',
  'impossible',
  'possible',
  'possible',
  'possible',
  'impossible'
]

まぁ、そうだよね。。。現実的な答えを返す ChatGPT なら impossible と答えるよね、、、

おや、待てよ?( ゚д゚)


いくつか possible が、、、ある、、、だと!?

"チャック・ノリスは一度に両方の方向に回転することができます。"
"チャック・ノリスは夜に眠りません。彼は待つだけです。"
"チャック・ノリスはカップラーメンを食べるのに2分も待ちません。"
"チャック・ノリスはサンタを信じていないがサンタはチャック・ノリスを信じている"
"チャック・ノリスは夢を見ることがあります。一度だけですが、その日以来、その夢は現実となっています。"
"チャック・ノリスが部屋に入ると、床が振るえます。それは恐怖でなく、敬意からです。"
"アメリカ合衆国の主な死因は 1.心臓病 2.チャック・ノリス 3.癌 である"
"チャック・ノリスの動くスピードは二種類。1.歩く 2.殺す"
"チャックノリスのピースサインは「あと二秒で殺す」の意味"
"アメリカ合衆国の主な死因は 1.心臓病 2.チャック・ノリス 3.癌 である"

Possible ????


というわけで最後はちょっと遊びましたがシュッと検証したのでおしまい。

この記事が気に入ったらサポートをしてみませんか?