『Gradioを使ったリテンションの理解』
『グラディオを活用したリテンションの理解』
Webアプリケーションを活用して分析する方法
私が初めてウェブアプリケーションを作った瞬間を覚えています。約8年前のことで、私はまだジュニアのアナリストであり、BIツールがすべての問題を解決できると確信していました。
エンジニアリングチームが新しいSDKのプロトタイプを作成し、データをより良く収集できるかどうかを知りたがっていました。彼らはデータを見て古いバージョンと比較するために、一連のデバイスでテストしました。しかし、デバイスの一連は絶えず変化していたため、BIツールで最新の状態を維持するにはかなりの作業が必要でした。そこで、私はウェブアプリケーションを作ることにしました。
私は一連の記事(正確には10から11記事)を見つけ、それらをすべて読んでこの知識を自分のタスクに役立てようとしました。最初のプロトタイプを完成させるまでに約1週間かかりました。バックエンドとフロントエンドの両方を書かなければならなかったので、少なくともジュニアのフルスタック開発者と自負できるようになりました。バックエンドにはFlaskを使用しました(Djangoにぶつかることがなかったのは幸運でした。そうでなければ1ヶ月かかっていたでしょう)、フロントエンドにはBootstrapとLeafletを使用しました。
全体的には、エンジニアリングに精通していくために多くの努力を必要とする難しい課題でした。自分の専門分野のすぐ隣にある他の分野についてもより深く理解することは常に価値があると考えています。
しかし、今では1時間未満でアナリストやデータサイエンティストがプロトタイプを作成できるツールがたくさんあり、私は喜んでいます。多くの場合、このようなプロトタイプは分析を次のレベルに引き上げることができます。以下にいくつかの例を示します:
- マーケティング予算や新機能をリリースする市場などの入力パラメータに応じた収益とオーディエンスの予測
- A/Bテストの計算機や自動の原因解析など、チームの作業を迅速化し、アドホックな作業量を減らすためのツール
- MVPソリューション。たとえば、内部プロセスの一部を自動化するためにLLMsを使用したい場合、本番版に時間を費やす前にプロトタイプをテストする価値があります。私は以前の記事でそのようなMLプロトタイプを共有しました。「1時間以内に自分の最初のディープラーニングアプリを作成する」。
この記事では、JavaScriptやCSSの問題に悩むことなく、迅速かつほぼ手間なく見栄えの良いウェブアプリケーションを作成するのに役立つフレームワークの1つについて紹介します。Gradioの基礎を学び、いくつかのウェブアプリケーションを開発し、HuggingFace Spacesに公開して誰でもアクセスできるようにします。
Gradioはそのようなフレームワークの唯一のものではありません。他にもいくつかのオープンソースのPythonの代替案があります:
- Streamlitは、コードをほとんど書かずにデータアプリを構築するための人気のあるパワフルなライブラリです。HuggingFace Spacesでもサポートされているため、そのようなアプリをホストできます。
- Dashは、すでにPlotlyに慣れている場合に便利であり、カスタマイズのためのさらなる機能を提供します。
- ただし、何かカスタムで複雑なものを構築したい場合、最終手段はFlaskまたはさらにはDjangoになります。
さまざまなフレームワークの主な機能に関する詳細は、この記事で確認できます。
Gradioの基本
Gradioは、インタラクティブなアプリケーションを構築するために使用されるオープンソースのPythonライブラリです。
Gradioの主な利点は次のとおりです:
- Pythonのみを使用してアプリケーションを構築できるため、アプリですべてのPythonライブラリを使用できること
- Jupyter Notebookまたは別のウェブページで実行できること
- GradioアプリをHuggingFace Spacesに永続的にホストできること
シルバー弾はありませんので、グラディオには制限があります:
- 明示的にMLアプリケーション用に設計されています。そのため、他の用途で使用する場合は、デフォルトを変更する必要がある場合があります(たとえば、
allow_flagging= "never"
でフラッグ付けをオフにするなど)。 - カスタマイズは限られており、特にデザインに関しては限られています。
- グラディオは主にクイックプロトタイピング用のフレームワークですので、いくつかの制約がありますが、大部分はうまく動作します。ただし、時々いくつかの奇妙な振る舞いに直面することもあります。例えば、Safariでの表の編集は直感に反して動作することがあり、また、インターフェースの読み込みのためにJupyter Notebookを再起動する必要があることもあります。
Gradioを使用するためには、Pythonパッケージをインストールする必要があります。
pip install gradio
昔ながらのプログラマの伝統に従って、「Hello, World!」から始めましょう。
gr.Interface
クラスを使用してインターフェースを定義します(ドキュメント)。これは、任意のPython関数に基づいてWebアプリケーションを作成するのに役立つGradioの中核クラスの1つです。
以下のパラメータを指定する必要があります:
inputs
: インターフェースの入力コンポーネント(この場合、テキストフィールドだけ)、outputs
: インターフェースの出力コンポーネント(この場合もテキストフィールドだけ)、fn
: コア機能(入力を受け取り出力を返す関数。この場合は入力から名前を受け取り、”Hello, <name>!”を返す関数)、title
&description
: アプリをユーザーフレンドリーにするためのマークダウン。
import gradio as grdemo = gr.Interface( inputs=[gr.Textbox(label="Name", lines=1)], outputs=[gr.Textbox(label="Result", lines=1)], fn=lambda x: 'Hello, %s!' % x, title="Hello, World!", description="Your first app using Gradio", allow_flagging='never')demo.launch()
このコードをJupyter Notebookで実行し、結果を確認できます。デバッグには非常に便利です。後ほど、自分のWebアプリケーションを他の人に利用できるようにする方法について説明します。
以上です。数行のコードだけで、最初のGradioアプリが実行されます。また、それなりに見栄えも良く、フロントエンドの魔法を使う必要もありませんでした。
Jupyter Notebookから作業している間に、Gradioはバックグラウンドで多くのプロセスを起動するため、時折
gr.close_all()
を使用して接続を閉じることが価値があります。
最も基本的な例を見て、Gradioの構築要素を見ました。これで、実際の解析タスクに進む準備ができました。
成長シミュレーション
最初の例として、製品の利用者の成長に対する維持率の影響を見てみましょう。
成長の基盤となる維持率
製品の成長を定義する2つのパラメータがあります:
- 獲得(各期間の新規ユーザー数)、
- 維持(製品内の顧客を維持する能力)。
維持率曲線に応じてユーザーベースがどのように成長するかをモデル化してみましょう。
パラメータ(a
、b
、c
、およびd
)のセットを使用して、任意の維持率曲線を次の関数で表現できます:
維持に関する最も一般的なケースについて説明しましょう。コホートは製品内の最初のアクションで定義され、すべてのアクションが維持に含まれます。その場合、periods = 0
の維持率は1として扱う必要があります(コホートの入場と維持イベントは同じ)。したがって、パラメータの1つを自動的に定義することができます。
成長の主要な要素は長期的なリテンションです。それは顧客が製品に長い間とどまるかどうか、製品が持続的に成長するか、顧客が1ヶ月で離脱し、成長のためにますます新しいユーザーを獲得する必要があるかを定義します。私たちの公式では、a
パラメータが長期的なリテンションを担当しています。
この公式を使用してリテンション曲線を定義できます。だから、開発に進むために必要なものはすべて揃っています。
リテンショングラフの可視化
まずは簡単に始めて、リテンション曲線のパラメータを取り、関係性をグラフとして表示するアプリケーションを作りましょう。
「Hello, World」という例と同じように、gr.Interface
クラスを使用し、inputs
、outputs
、fn
をマップします。
- 今回はより多くの入力パラメータが必要です。したがって、
inputs
はコントロールのリストになります。gr.Slider
とgr.Dropdown
のコントロールを使用します。gr.Sliderの場合、最小値、最大値、デフォルト値、および関数内で使用するラベルを指定する必要があります。gr.Dropdownの場合、可能な値のリスト、デフォルト値、およびラベルを定義する必要があります。 - 出力はまだ1つだけです – プロットなので、
outputs
はパラメータなしでgr.Plot
となります。 - 関数
fn
は入力を出力にマップするため、入力引数を受け取り、plotly.Figure
オブジェクトを返します。
import plotly.express as px# リテンションを計算する関数def get_retention(a, b, c, d, periods): return a + 1./(b + c * (periods ** d))def get_retention_same_event(a, c, d, periods): b = 1./(1 - a) return get_retention(a, b, c, d, periods)# 関数を定義 - 入力パラメータに応じたプロットを返すdef get_retention_plot(a, c, d, num_periods): df = pd.DataFrame({'x': range(num_periods + 1)}) df['retention'] = df.x.map(lambda x: get_retention_same_event(a, c, d, x)) return px.line(df, x = 'x', y = 'retention', color_discrete_sequence = px.colors.qualitative.Prism, title = 'リテンション曲線', labels = {'x': '期間'})# 入力を定義inputs = [ gr.Slider(0, 1, 0.03, label="a"), gr.Slider(0, 5, 0.55, label="c"), gr.Slider(0, 5, 1.5, label="d"), gr.Dropdown([10, 30, 60, 90], value = 30, label="期間の数"), gr.Dropdown([10, 100, 1000, 10000], value = 10000, label="各期間の新規ユーザー数")]# 出力を定義outputs = gr.Plot()# インターフェースを定義demo = gr.Interface( fn=get_retention_plot, inputs=inputs, outputs=outputs, cache_examples=True, allow_flagging = 'never' # インターフェース内で既定のフラグ機能を非表示にする)# アプリを起動demo.launch(debug = True)
このアプリを実行してみましょう。動作しています – 新しいパラメータを送信するとグラフが変化することが分かります。
さらなるグラフの追加
リテンションの成長への影響を見ることが目標だったので、リテンションだけでなく時間経過に伴うオーディエンスも表示するグラフを追加する必要があります。インターフェースを変更しましょう。
簡単にするために、各期間において、同じ数の新規ユーザーが製品を使用すると考えます(cohort_size
パラメーター)。
実装に少しの変更が必要です:
get_retention_plot
関数を変更し、コホートサイズのための1つの追加パラメーターを取得し、時間経過に伴うユーザー数を計算し、3つの図を返すようにします。- パラメーター
outputs
は、今は3つのgr.Plot()
オブジェクトのリストと等しいです。
def get_retention_plot(a, c, d, num_periods, cohort_size): ret_df = pd.DataFrame({'x': range(num_periods + 1)}) ret_df['retention'] = ret_df.x.map(lambda x: get_retention_same_event(a, c, d, x)) ret_fig = px.line(ret_df.iloc[1:], x = 'x', y = 'retention', color_discrete_sequence = px.colors.qualitative.Prism, title = 'Retention curve') # simulation tmp_data = [] for cohort in range(num_periods + 1): for cohort_period in range(num_periods + 1): period = cohort_period + cohort if period > num_periods: continue retention = get_retention_same_event(a, c, d, cohort_period) tmp_data.append( { 'cohort': 'cohort %s' % str(cohort).rjust(3, '0'), 'cohort_period': cohort_period, 'period': period, 'retention': retention, 'users': int(round(retention * cohort_size)) } ) users_df = pd.DataFrame(tmp_data) users_fig = px.area(users_df.groupby('period').users.sum(), color_discrete_sequence = px.colors.qualitative.Prism, title = 'Active users') cohorts_fig = px.area(users_df.pivot_table(index = 'period', columns = 'cohort', values = 'users', aggfunc = 'sum'), color_discrete_sequence = px.colors.qualitative.Prism, title = 'Active users by cohorts') return ret_fig, users_fig, cohorts_figinputs = [ gr.Slider(0, 1, 0.03, label="a"), gr.Slider(0, 5, 0.55, label="c"), gr.Slider(0, 5, 1.5, label="d"), gr.Dropdown([10, 30, 60, 90], value = 30, label="Number of Periods"), gr.Dropdown([10, 100, 1000, 10000], value = 10000, label="Number of new users each period")]outputs = [gr.Plot(), gr.Plot(), gr.Plot()]demo = gr.Interface( fn=get_retention_plot, inputs=inputs, outputs=outputs, allow_flagging = 'never', cache_examples=True,)demo.launch(debug = True)
素晴らしい、これで全体像を見ることができ、関係を分析することができます。ただし、改善の余地があります。アプリを使いやすくするためにフォーマットを追加することができます。
スタイルの追加
私たちは、インターフェースを少し調整して、ユーザーフレンドリーでわかりやすくします。
そのために、gr.Blocks()
をコンテキストとして使用します。これにより、よりカスタムの Web アプリケーションを作成し、レイアウトやデータフロー(関数をトリガーし、それに続く実行を引き起こすイベント)を定義することができます。
Blocks は次のような新たな機会を提供してくれます:
gr.Blocks()
を使用することで、gr.Row()
やgr.Column()
を使ってレイアウトを整理することができます。gr.Markdown
を使って、タイトルや数式を含む Markdown 要素を追加することができます(デフォルトでは、数式は$の内側に置く必要があります)。gr.Accordion
を使うことで、デフォルトでユーザーに表示したくない一部のパラメーターを非表示にすることができます。- また、このアプローチでは、更新のより複雑なロジックを定義することもできます。たとえば、サブミットボタンだけでなく、任意の入力パラメーターの変更時にプロットを更新することができます。この機能を以下の例で使用します。
ブロックを使用する場合、各入力と出力を変数として定義する必要があります。例えば、a = gr.Slider(0, 1, 0.03, label=”a”)
となります。
また、デフォルトのコントロールが存在しないため、ボタンを自分で定義する必要があります。例えば、btn_caption = gr.Button(“Submit”)
となります。
ボタンがクリックされた際のアクションも指定する必要があります。既に馴染みのあるパラメーターであるinputs
、outputs
、fn
を設定します。
btn_caption.click(fn=get_retention_plot, inputs=[a, c, d, num_periods, cohort_size], outputs=[plot1, plot2, plot3])
以下はコードの完全なバージョンです。
with gr.Blocks() as demo:
gr.Markdown("# Understanding Growth 🚀")
with gr.Row():
with gr.Column():
gr.Markdown("## Retention curve parameters 📈")
gr.Markdown(r"$\textbf{retention}(\textsf{x}) = \textsf{a} + \frac{\textsf{1}}{\textsf{b} + \textsf{c} * \textsf{x}^{\textsf{d}}}\ where\ \textsf{b} = \frac{\textsf{1}}{\textsf{1}-\textsf{a}}$")
with gr.Row():
a = gr.Slider(0, 1, 0.03, label="a")
c = gr.Slider(0, 5, 0.55, label="c")
d = gr.Slider(0, 5, 1.5, label="d")
with gr.Accordion("More options", open=False):
with gr.Row():
num_periods = gr.Dropdown([10, 30, 60, 90], value = 30, label="Number of Periods")
cohort_size = gr.Dropdown([10, 100, 1000, 10000], value = 10000, label="Number of new users each period")
btn_caption = gr.Button("Submit")
with gr.Column():
plot1 = gr.Plot()
with gr.Row():
plot2 = gr.Plot()
plot3 = gr.Plot()
btn_caption.click(fn=get_retention_plot, inputs=[a, c, d, num_periods, cohort_size], outputs=[plot1, plot2, plot3])
demo.launch()
アプリをホスティングする
HuggingFace Spacesを利用すると、ウェブアプリケーションをホスティングし、簡単に他の人と共有することができます。
Spacesを使用するには、アカウントが必要です。まだ登録していない場合は、このリンクをフォローしてください。登録には数分しかかかりません。
次のステップは、新しいスペースを作成することです。詳細な手順については、ドキュメントをご覧ください。
新しいスペースの場合、次のパラメーターを入力する必要があります:名前、ライセンス、SDKとしてGradioを選択してください。
その後、Hugging SpacesのGitリポジトリにコードをコミットする必要があります。まず、リポジトリをクローンする必要があります。
-- cloning repogit clone https://huggingface.co/spaces/<your_login>/<your_app_name>cd <your_app_name>
最近、HuggingFaceはGit認証プロセスを変更しましたので、まずトークンを作成し、Gitリポジトリに設定する必要があります。
git remote set-url origin https://<your_login>:<token>@huggingface.co/spaces/<your_login>/<your_app_name>git pull origin
さあ、アプリケーションに関連するファイルをコミットする時が来ました。少なくとも以下のファイルが必要です:
app.py
:Gradioアプリを起動するPythonコードですrequirements.txt
:アプリケーションに必要なPythonパッケージのリストです。私たちの場合は、pandas
とplotly
のみです。
次に、gitの基本的な手順:追加、コミット、HuggingFacesにプッシュします。
git add app.pygit add requirements.txtgit commit -m 'リテンションシミュレータアプリの最初のバージョン'git push
アプリのビルドに数分かかり、完了しました。これで、ウェブアプリケーションがHuggingFaces Spacesで実行されています。こちらでお試しください。
この初期バージョンと比べて、レイアウトがスクロールを必要としないため、外観がはるかに良くなりました。また、ユーザーはパラメータa
、c
、d
が何を意味するかを推測する必要はありません。
リテンションの予測
ウェブアプリケーションでいくつかのパラメータに基づいてグラフを生成する方法を学びました。しかし、実際の生活では、通常、かなり多くのデータを入力する必要があります。したがって、.csv
ファイルのデータをアプリで使用する方法を見てみましょう。
例として、最初の数期間の実際のリテンションデータを参照し、次の期間のリテンションを予測してみましょう。これは非常に一般的なタスクであり、新しいコホートの3ヶ月目のリテンションを比較するために3ヶ月待つことは通常望ましくありません。事実のデータを.csv
ファイルとしてアップロードします。
時間を無駄にせずに、実装に移りましょう。
ファイルからデータを取得する
以下は、全体のインターフェースとビジネスロジックを生成するためのコードです。少し複雑に見えるかもしれませんが、心配しないでください。後で核心のポイントについて説明します。
# ファイルまたは文字列を解析し、データフレームを返すdef parse_file(input_text_or_file, num_periods): if isinstance(input_text_or_file, str): df = pd.read_csv(StringIO(input_text_or_file), sep = '\t') else: df = pd.read_csv(input_text_or_file.name, sep = '\t') return df# データフレームを取得し、プロットを返すdef show_graph_for_df(df, num_periods): df['period'] = df.period.map(int) df['retention_fact'] = df.retention_fact.map(float) result = scipy.optimize.minimize(lambda x: get_mse_for_retention(x, df), [random.random(), random.random(), random.random()]) a, c, d = result.x pred_df = pd.DataFrame({'period': range(num_periods + 1)}) pred_df['retention_pred'] = pred_df.period.map(lambda x: get_retention_same_event(a, c, d, x)) pred_df = pred_df.merge(df, how = 'left') fig = go.Figure() fig.add_trace(go.Scatter(x=pred_df.period, y=pred_df.retention_fact, name='実績', line=dict(color=plotly.colors.qualitative.Prism[0], width=3))) fig.add_trace(go.Scatter(x=pred_df.period, y=pred_df.retention_pred, name='予測', line=dict(color=plotly.colors.qualitative.Prism[0], width=3, dash='dot'))) fig.update_layout(title='デイリーリテンションモデル (a = %.2f, c = %.2f, d = %.2f)' % (a, c, d), yaxis_title='リテンション', xaxis_title='期間') return fig# ファイルを取得し、プロットを返すdef show_graph_for_file(temp_file, num_periods): df = parse_file(temp_file, num_periods) return show_graph_for_df(df, num_periods)# データのハードコード例default_csv = '期間\tリテンション実績\n0\t1\n1\t0.55\n2\t0.4\n3\t0.35\n4\t0.3\n'# gr.Blocks()とのインターフェースとして使用with gr.Blocks() as demo: gr.Markdown('# リテンション曲線の予測 📊') periods = gr.Dropdown([10, 30, 90, 180], label="期間の数", value = 30) gr.Markdown('.csvファイルをアップロードしてデータを使用するか、デフォルトデータを例として使用するか、アップロードされたデータセクションで数値を手動で入力してください。') gr.Markdown('''__ファイル形式:__ 2列 (`期間`と`リテンション実績`)''') with gr.Row(): upload_button = gr.UploadButton(label="ファイルをアップロード", file_types = ['.csv'], live=True, file_count = "single") default_button = gr.Button('例を表示') with gr.Row(): with gr.Accordion("アップロードされたデータ", open=False): gr.Markdown('表の値を変更できます') table = gr.Dataframe(type="pandas", col_count=2, interactive = True, headers = ['期間', 'リテンション実績']) with gr.Row(): image = gr.Plot() # トリガーやイベントのビジネスロジック upload_button.upload(fn=show_graph_for_file, inputs=[upload_button, periods], outputs=image, api_name="upload_graph") upload_button.upload(fn=parse_file, inputs=[upload_button, periods], outputs=table, api_name="upload_csv") default_button.click(fn=lambda x: show_graph_for_file(default_csv, x), inputs=[periods], outputs=image, api_name="upload_example_graph") default_button.click(fn=lambda x: parse_file(default_csv, x), inputs=[periods], outputs=table, api_name="upload_example_csv") table.change(fn=show_graph_for_df, inputs=[table, periods], outputs=image, api_name="upload_table_graph") periods.change(fn=show_graph_for_df, inputs=[table, periods], outputs=image, api_name="upload_periods_graph")demo.launch(debug=True)
近くで見てみましょう。インターフェースには以下の要素があります:
periods
— 入力パラメーターupload_button
—.csv
ファイルからデータを読み込むための入力パラメーターdefault_button
— 例として事前に定義された値でテーブルとグラフを更新するためのボタンtable
— アップロードされたデータ(.csv
ファイルまたは例から)のデータフレームを表示します。また、テーブル内の数値を変更するとグラフも更新されますので、これも入力パラメーターですimage
— プロットを表示する出力パラメーターです
parse_file
関数は、upload_button
からファイルを取得するか、デフォルトの例から文字列を取得し、pandas
データフレームを返します。そのため、ファイルからデータを使用するのは非常に簡単です。
重要なビジネスロジックは以下のコードスニペットで定義されています。すべてのインターフェース要素に対するアクションが定義されています:
.csv
ファイルをアップロードすると、テーブルとグラフが更新されます- 「例を表示」ボタンをクリックすると、テーブルとグラフが更新されます
- テーブル内のデータを変更すると、グラフだけが更新されます
- 期間の数を変更すると、グラフだけが更新されます
upload_button.upload(fn=show_graph_for_file, inputs=[upload_button, periods], outputs=image, api_name="upload_graph")upload_button.upload(fn=parse_file, inputs=[upload_button, periods], outputs=table, api_name="upload_csv")default_button.click(fn=lambda x: show_graph_for_file(default_csv, x), inputs=[periods], outputs=image, api_name="upload_example_graph")default_button.click(fn=lambda x: parse_file(default_csv, x), inputs=[periods], outputs=table, api_name="upload_example_csv")table.change(fn=show_graph_for_df, inputs=[table, periods], outputs=image, api_name="upload_table_graph")periods.change(fn=show_graph_for_df, inputs=[table, periods], outputs=image, api_name="upload_periods_graph")
最適な適合関数の定義
私たちの解決策の重要な部分は、実データに対して最適な適合関数を見つけることです。どのように行うか見てみましょう。
- まず、関数
get_mse_for_retention
を定義します。この関数は、パラメーターのセット(a
、c
、d
)に対するエラーを返します。また、データフレームを入力として受け取ります。 - エラーとして標準的な平均二乗誤差(MSE)を使用します。
- その後、最適化のために
scipy.optimize.minimize
関数を使用します。最適化する関数(データフレームはハードコードされているため、パラメーターのみを最適化しています)と、パラメーターの初期値(ランダムな値のリスト)を渡す必要があります。 - 最適化後、最適なパラメーターには
result.x
を使用してアクセスできます。
def get_mse_for_retention(params, df): tmp_df = df.copy() tmp_df['retention_pred'] = tmp_df.index.map( lambda x: get_retention_same_event(params[0], params[1], params[2], x) ) tmp_df['se'] = (tmp_df.retention_fact - tmp_df.retention_pred) tmp_df['se'] = tmp_df['se']**2 return tmp_df.se.mean() ** 0.5result = scipy.optimize.minimize(lambda x: get_mse_for_retention(x, df), [random.random(), random.random(), random.random()])a, c, d = result.xprint(a, c, d)
以上です。これで、実データに対する理論的な維持曲線を把握し、アプリで予測に使用することができます。
最後のステップ
私は同じ手順に従って、このアプリをHuggingFace Spacesに投稿しました。したがって、ここでそれを試すことができます。
両方のアプリの完全なコードはGitHubで見つけることができます。
概要
この記事では、Gradioライブラリの基本を学び、Pythonだけで楽しいWebアプリケーションを作成する方法を学びました。
私たちはいくつかのアプローチを学びました:
- 高レベルの
gr.Interface
クラスは、素早く動作するプロトタイプを作成することができます。 - 入力と出力の間の複雑な関係を定義し、必要なレイアウトを指定することができる
gr.Blocks
を使用したカスタマイズ可能な方法。
この記事をお読みいただきありがとうございます。あなたにとって有益であったことを願っています。追加の質問やコメントがある場合は、コメントセクションにお書きください。
参考文献
この記事は「Gradioを使用したジェネレーティブAIアプリケーションの構築」コースに触発されました。
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles