kevineen commited on
Commit
7fcccde
1 Parent(s): edbc65c
Files changed (5) hide show
  1. .gitignore +1 -9
  2. README.md +6 -6
  3. app.py +441 -0
  4. note.txt +1 -1
  5. requirements.txt +1 -2
.gitignore CHANGED
@@ -161,12 +161,4 @@ cython_debug/
161
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
  #.idea/
163
 
164
- user_annotation/*
165
-
166
- run_2.py
167
- run_3.py
168
- run_4.py
169
- backup.py
170
- idea.txt
171
-
172
- dataclass.py
 
161
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
  #.idea/
163
 
164
+ user_annotation/*
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
- title: tanuki_annotation_phase2
3
- emoji: 💬
4
- colorFrom: yellow
5
- colorTo: purple
6
  sdk: gradio
7
- app_file: run.py # HotReloadデバッグのため、app.pyから変更中 gradio run.pyで開発中は変更の監視が可能
8
  pinned: false
9
- license: apache-2.0
10
 
11
  hf_oauth: true
12
  # optional, default duration is 8 hours/480 minutes. Max duration is 30 days/43200 minutes.
 
1
  ---
2
+ title: Tanuki Annotation Phase2
3
+ emoji: 📊
4
+ colorFrom: red
5
+ colorTo: red
6
  sdk: gradio
7
+ app_file: app.py
8
  pinned: false
9
+ license: unknown
10
 
11
  hf_oauth: true
12
  # optional, default duration is 8 hours/480 minutes. Max duration is 30 days/43200 minutes.
app.py ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import json
4
+ import datetime
5
+ from pathlib import Path
6
+ import uuid
7
+ from typing import Tuple
8
+
9
+ import pandas as pd
10
+
11
+ import gradio as gr
12
+ from datasets import load_dataset
13
+ from huggingface_hub import CommitScheduler
14
+ from huggingface_hub import HfFolder
15
+
16
+
17
+ # HF_Spaceでプライベート関連にアクセスするための環境変数
18
+ # SecretKey をSpaceのSettingsに設定
19
+ HF_TOKEN = os.getenv("HF_TOKEN")
20
+ if HF_TOKEN:
21
+ HfFolder.save_token(HF_TOKEN)
22
+ else:
23
+ print("Warning: HF_TOKEN not found. Please set it in your Space secrets.")
24
+
25
+ # HFデータセット アップロード先
26
+ # (切替てテストする用に配列)
27
+ output_dataset = [
28
+ "team-hatakeyama-phase2/annotation_tanuki_phase2",
29
+ "kevineen/Tanuki-Phase2-annotation-dataset", # test
30
+ ]
31
+
32
+ # アノテーション対象データセット
33
+ annotation_dataset_list = [
34
+ "hatakeyama-llm-team/AutoGeneratedJapaneseQA",
35
+ "hatakeyama-llm-team/AutoGeneratedJapaneseQA-other",
36
+ "kanhatakeyama/ChatbotArenaJaMixtral8x22b",
37
+ "kanhatakeyama/OrcaJaMixtral8x22b",
38
+ "kanhatakeyama/LogicalDatasetsByMixtral8x22b",
39
+
40
+ # データ形式未対応(対応予定
41
+ # "hatakeyama-llm-team/WikiBookJa",
42
+ # "kanhatakeyama/AutoWikiQA",
43
+ # "susumuota/SyntheticTextWikiTranslate-askllm-v1", # Ask-LLM 翻訳
44
+ ]
45
+
46
+ multi_turn_annotation_dataset_list = [
47
+ # マルチターン 未対応
48
+ "kanhatakeyama/AutoMultiTurnByMixtral8x22b",
49
+ ]
50
+
51
+ # Session State : (ブラウザセッション単位の変数管理) ===========================
52
+
53
+ # UIのEnable/Disable用State
54
+ is_selected_dataset = gr.State(False)
55
+ is_loaded_dataset = gr.State(False)
56
+
57
+ # 選択中のデータセットリスト
58
+ dropdown_dataset_list = gr.State(value=annotation_dataset_list)
59
+ # 現在の対象データセット 初期値は"hatakeyama-llm-team/AutoGeneratedJapaneseQA",
60
+ select_dropdown_dataset = gr.State(dropdown_dataset_list.value[0])
61
+ select_dataset = gr.State(None) # 現在のデータセット
62
+ select_dataset_total_len = gr.State(0) # 現在のデータセットの長さ
63
+ select_idx = gr.State(0) # 現在のインデックス (ランダムモードにするなら不要?
64
+ random_mode = gr.State(False)
65
+
66
+ # 回答者がアノテーションしたデータセット
67
+ annotated_dataset = gr.State(
68
+ pd.DataFrame({
69
+ 'dataset_name': [],
70
+ 'dataset_id': [],
71
+ 'who': [],
72
+ 'good': [],
73
+ 'bad': [],
74
+ 'score': [],
75
+ 'is_proofreading_1': [],
76
+ 'answer_text_1': [],
77
+ 'is_proofreading_2': [], # マルチターン用
78
+ 'answer_text_2': [], # マルチターン用
79
+ })
80
+ )
81
+
82
+ initial_answer_text_1 = gr.State("") # 回答1を整形したかチェック用
83
+ initial_answer_text_2 = gr.State("") # 回答2を整形したかチェック用
84
+
85
+ is_dataset_loaded = gr.State(False)
86
+
87
+ you_dataset_id = gr.State(0) # 回答者がアノテーションしているデータのID
88
+ dataset_name = gr.State("") # 編集に使用したデータセット名
89
+ dataset_id = gr.State(0) # 加工元データセットのindex
90
+ who = gr.State("") # アノテーション者名
91
+ good = gr.State(False) # 良
92
+ bad = gr.State(False) # 悪
93
+ score = gr.State(3) # スコア 初期値は3
94
+ is_proofreading_1 = gr.State(False) # 回答1を整形したか_1
95
+ answer_text_1 = gr.State("") # answer_1 回答
96
+ is_proofreading_2 = gr.State(False) # 回答2を整形したか_2
97
+ answer_text_2 = gr.State("") # answer_2 回答
98
+
99
+ # 未整理
100
+ # データ読み込み ========================================
101
+
102
+ def dataset_load_fn() -> Tuple[
103
+ str,
104
+ str,
105
+ str,
106
+ str,
107
+ gr.update,
108
+ gr.update,
109
+ gr.update,
110
+ gr.update,
111
+ gr.update,
112
+ gr.update,
113
+ gr.update]:
114
+
115
+ is_dataset_loaded.value = False # ロード状態
116
+
117
+ select_dataset.value = load_dataset(
118
+ select_dropdown_dataset.value
119
+ )
120
+
121
+ # DatasetオブジェクトをPandas DataFrameに変換
122
+ df = select_dataset.value["train"].to_pandas()
123
+
124
+ # index列を追加し、シャッフル
125
+ df = df.reset_index(drop=False) # 元のindexを保持
126
+ df = df.sample(frac=1).reset_index(drop=True) # シャッフル
127
+ select_dataset.value["train"] = df # シャッフルされたDataFrameを格納
128
+
129
+ select_idx.value = 0 # index初期化
130
+ select_dataset_total_len.value = len(df) # 長さを取得
131
+ is_dataset_loaded.value = True # ロード完了
132
+
133
+ # データロード時に初期値を設定
134
+ initial_answer_text_1.value = df.iloc[select_idx.value]["answer"]
135
+ initial_answer_text_2.value = df.iloc[select_idx.value]["answer"]
136
+
137
+ return df.iloc[select_idx.value]["question"], \
138
+ df.iloc[select_idx.value]["answer"], \
139
+ df.iloc[select_idx.value]["question"], \
140
+ df.iloc[select_idx.value]["answer"], \
141
+ gr.update(interactive=True), \
142
+ gr.update(interactive=True), \
143
+ gr.update(interactive=True), \
144
+ gr.update(interactive=True), \
145
+ gr.update(interactive=True), \
146
+ gr.update(interactive=True), \
147
+ gr.update(interactive=True)
148
+
149
+
150
+ # データの保存処理 ========================================
151
+
152
+ # Spaceの場合の保存先はCommitSchedulerのpath_in_repoフォルダ
153
+ # (ローカル開発の場合./user_annotationフォルダにjsonファイルが作成される)
154
+ annotation_file = Path("user_annotation/") / f"data_{uuid.uuid4()}.json"
155
+ annotated_folder = annotation_file.parent
156
+
157
+ scheduler = CommitScheduler(
158
+ repo_id=output_dataset[0],
159
+ repo_type="dataset",
160
+ folder_path=annotated_folder,
161
+ path_in_repo="data", # Spaceの場合の保存先フォルダー
162
+ private=True,
163
+ every=5, # 5分毎にアップロード HuggingFAce_Documentの最低推奨値
164
+ )
165
+
166
+ # CommitScheduler (HFへのデータアップロード
167
+ def save_annotation(
168
+ dataset_name: str,
169
+ dataset_id: int,
170
+ who: str,
171
+ good: bool,
172
+ bad: bool,
173
+ score: int,
174
+ is_proofreading_1: bool,
175
+ answer_text_1: str,
176
+ is_proofreading_2: bool,
177
+ answer_text_2: str) -> None:
178
+
179
+ annotated_dataset.value = pd.concat([
180
+ annotated_dataset.value,
181
+ pd.DataFrame({
182
+ 'dataset_name': [dataset_name],
183
+ 'dataset_id': [dataset_id],
184
+ 'who': [who],
185
+ 'good': [good],
186
+ 'bad': [bad],
187
+ 'score': [score],
188
+ 'is_proofreading_1': [is_proofreading_1],
189
+ "answer_text_1": [answer_text_1],
190
+ 'is_proofreading_2': [is_proofreading_2],
191
+ 'answer_text_2': [answer_text_2]
192
+ })], ignore_index=True).reset_index(drop=True)
193
+
194
+ # 書き込み
195
+ with scheduler.lock:
196
+ with annotation_file.open("a", encoding='utf-8') as f:
197
+ data_to_write = {
198
+ # "id": , CommitSchedulerだと取得して末尾idを付与することが無理?
199
+ "datetime": str(datetime.datetime.now().isoformat()),
200
+ "dataset_name": dataset_name,
201
+ "dataset_id": int(dataset_id),
202
+ "who": who,
203
+ "good": good,
204
+ "bad": bad,
205
+ "score": score,
206
+ "is_proofreading_1": is_proofreading_1,
207
+ "answer_text_1": answer_text_1,
208
+ "is_proofreading_2": is_proofreading_2,
209
+ "answer_text_2": answer_text_2,
210
+ }
211
+ f.write(json.dumps(data_to_write, ensure_ascii=False))
212
+ f.write("\n")
213
+
214
+ # アノテーションの追加処理 ========================================
215
+
216
+
217
+ # UI処理 ========================================
218
+
219
+ # ユーザー名表示
220
+ def hello(profile: gr.OAuthProfile | None) -> Tuple[str, str]:
221
+ if profile is None:
222
+ return "プライベートデータセット取得のためにログインしてください。", who.value
223
+ who.value = profile.username
224
+ return f'{profile.username} さん、よろしくお願いいたします。', who.value
225
+
226
+
227
+ # テーマの状態
228
+ theme_ = gr.themes.Default()
229
+
230
+ # 後のCSSデザイン変更用
231
+
232
+
233
+ def load_css():
234
+ with open("style.css", "r") as file:
235
+ css_content = file.read()
236
+ return css_content
237
+
238
+
239
+ # Gradio 画面 ============================================
240
+ with gr.Blocks(theme=theme_, css=load_css()) as demo:
241
+
242
+ gr.Markdown("# データセット アノテーション for Tanuki (Phase2)")
243
+
244
+ with gr.Tab("アノテーション (シングルターン)"):
245
+
246
+ with gr.Row(equal_height=True):
247
+
248
+ gr.LoginButton(value="HuggingFace ログイン",
249
+ logout_value="HuggingFace ログアウト", scale=1)
250
+
251
+ # ユーザー名
252
+ gr_profile_name = gr.Markdown()
253
+ demo.load(hello, inputs=None, outputs=[gr_profile_name, who])
254
+
255
+ with gr.Row():
256
+
257
+ def dropdown_select(select_value) -> None:
258
+ select_dropdown_dataset.value = select_value
259
+
260
+ # 対象データセットの選択
261
+ gr_dropdown_dataset = gr.Dropdown(
262
+ label="データセット選択 ①",
263
+ choices=dropdown_dataset_list.value,
264
+ value=select_dropdown_dataset.value,
265
+ elem_id="dataset_sel",
266
+ scale=2)
267
+
268
+ gr_dropdown_dataset.change(
269
+ dropdown_select,
270
+ inputs=[gr_dropdown_dataset]
271
+ )
272
+
273
+ gr_data_load_btn = gr.Button("② データセットを読み込む")
274
+
275
+ with gr.Column() as content_column:
276
+ with gr.Tab("③ シンプル(良・悪)"):
277
+ with gr.Column():
278
+ with gr.Row(equal_height=True):
279
+ good_btn = gr.Button("良い", interactive=False)
280
+ bad_btn = gr.Button("悪い", interactive=False)
281
+
282
+ gr_question_text_1 = gr.Textbox(
283
+ label="質問: ", lines=5, interactive=False)
284
+
285
+ gr_answer_text_1 = gr.Textbox(
286
+ label="回答: 訂正頂けると品質が��がります。",
287
+ lines=20,
288
+ interactive=True)
289
+
290
+ with gr.Tab("③ 5段階評価"):
291
+
292
+ gr_question_text_3 = gr.Textbox(
293
+ label="質問: ", lines=5, interactive=False)
294
+
295
+ with gr.Row() as score_btn:
296
+ gr_score_1 = gr.Button("1: 低品質", interactive=False)
297
+ gr_score_2 = gr.Button("2: 悪い", interactive=False)
298
+ gr_score_3 = gr.Button("3: 普通", interactive=False)
299
+ gr_score_4 = gr.Button("4: 良い", interactive=False)
300
+ gr_score_5 = gr.Button("5: 高品質", interactive=False)
301
+
302
+ gr_answer_text_3 = gr.Textbox(
303
+ label="回答: 訂正して頂けると品質が上がります。", lines=20, interactive=True)
304
+
305
+ # 5段階評価ボタンのクリックイベントを定義
306
+ def score_button_clicked(button_value):
307
+ good.value = False
308
+ bad.value = False
309
+ score.value = button_value
310
+
311
+ gr_data_load_btn.click(
312
+ dataset_load_fn,
313
+ inputs=None,
314
+ outputs=[gr_question_text_1,
315
+ gr_answer_text_1,
316
+ gr_question_text_3,
317
+ gr_answer_text_3,
318
+ good_btn,
319
+ bad_btn,
320
+ gr_score_1,
321
+ gr_score_2,
322
+ gr_score_3,
323
+ gr_score_4,
324
+ gr_score_5,
325
+ ]
326
+ )
327
+
328
+ def update_annotation(
329
+ input_ans_1: str = None,
330
+ input_ans_3: str = None,
331
+ is_good: bool = None, # good/bad を表すフラグを追加
332
+ score_value: int = None # 5段階評価の値、good/badの場合はNone
333
+ ) -> Tuple[gr.update, gr.update, gr.update, gr.update]:
334
+
335
+ # good/bad と score の状態を更新
336
+ if score_value is not None: # 5段階評価の場合
337
+ good.value = False
338
+ bad.value = False
339
+ score.value = score_value
340
+ else: # good/bad評価の場合
341
+ good.value = is_good
342
+ bad.value = not is_good
343
+
344
+ # 変更を検知 (5段階評価の場合も処理するように変更)
345
+ if input_ans_1 is not None and initial_answer_text_1.value != input_ans_1:
346
+ is_proofreading_1.value = True
347
+ answer_text_1.value = input_ans_1
348
+ else:
349
+ answer_text_1.value = ""
350
+
351
+ if input_ans_3 is not None and initial_answer_text_2.value != input_ans_3:
352
+ is_proofreading_2.value = True
353
+ answer_text_2.value = input_ans_3
354
+ else:
355
+ answer_text_2.value = ""
356
+
357
+ # 表示更新
358
+ # indexを進める
359
+ select_idx.value += 1
360
+
361
+ df = select_dataset.value["train"]
362
+
363
+ # ループさせるか、エラー処理を行う
364
+ if select_idx.value >= len(df):
365
+ select_idx.value = 0
366
+
367
+ # データセットに追加
368
+ # 元のindex番号(dataset_id)を指定して保存
369
+ save_annotation(
370
+ select_dropdown_dataset.value,
371
+ # datasetIdは元のindex番号を使用
372
+ df.iloc[select_idx.value]['index'],
373
+ who.value,
374
+ good.value,
375
+ bad.value,
376
+ score.value,
377
+ is_proofreading_1.value,
378
+ answer_text_1.value,
379
+ is_proofreading_2.value,
380
+ answer_text_2.value
381
+ )
382
+
383
+ # Nextデータ初期化
384
+ is_proofreading_1.value = False
385
+ is_proofreading_2.value = False
386
+ initial_answer_text_1.value = df.iloc[select_idx.value]["answer"]
387
+ initial_answer_text_2.value = df.iloc[select_idx.value]["answer"]
388
+
389
+ return gr.update(value=df.iloc[select_idx.value]["question"]), \
390
+ gr.update(value=df.iloc[select_idx.value]["answer"]), \
391
+ gr.update(value=df.iloc[select_idx.value]["question"]), \
392
+ gr.update(value=df.iloc[select_idx.value]["answer"])
393
+
394
+ def good_click(input_ans_1, input_ans_3):
395
+ return update_annotation(input_ans_1=input_ans_1, input_ans_3=input_ans_3, is_good=True)
396
+
397
+ good_btn.click(
398
+ good_click,
399
+ inputs=[
400
+ gr_answer_text_1,
401
+ gr_answer_text_3
402
+ ],
403
+ outputs=[gr_question_text_1,
404
+ gr_answer_text_1,
405
+ gr_question_text_3,
406
+ gr_answer_text_3]
407
+ )
408
+
409
+ def bad_click(input_ans_1, input_ans_3):
410
+ return update_annotation(input_ans_1=input_ans_1, input_ans_3=input_ans_3, is_good=False)
411
+
412
+ bad_btn.click(
413
+ bad_click,
414
+ inputs=[
415
+ gr_answer_text_1,
416
+ gr_answer_text_3
417
+ ],
418
+ outputs=[gr_question_text_1,
419
+ gr_answer_text_1,
420
+ gr_question_text_3,
421
+ gr_answer_text_3]
422
+ )
423
+
424
+ # 5段階評価ボタンのクリックイベント
425
+ gr_score_1.click(lambda x: update_annotation(input_ans_1=x, input_ans_3=x, score_value=1),
426
+ inputs=[gr_answer_text_3], outputs=[gr_question_text_1, gr_answer_text_1, gr_question_text_3, gr_answer_text_3])
427
+ gr_score_2.click(lambda x: update_annotation(input_ans_1=x, input_ans_3=x, score_value=2),
428
+ inputs=[gr_answer_text_3], outputs=[gr_question_text_1, gr_answer_text_1, gr_question_text_3, gr_answer_text_3])
429
+ gr_score_3.click(lambda x: update_annotation(input_ans_1=x, input_ans_3=x, score_value=3),
430
+ inputs=[gr_answer_text_3], outputs=[gr_question_text_1, gr_answer_text_1, gr_question_text_3, gr_answer_text_3])
431
+ gr_score_4.click(lambda x: update_annotation(input_ans_1=x, input_ans_3=x, score_value=4),
432
+ inputs=[gr_answer_text_3], outputs=[gr_question_text_1, gr_answer_text_1, gr_question_text_3, gr_answer_text_3])
433
+ gr_score_5.click(lambda x: update_annotation(input_ans_1=x, input_ans_3=x, score_value=5),
434
+ inputs=[gr_answer_text_3], outputs=[gr_question_text_1, gr_answer_text_1, gr_question_text_3, gr_answer_text_3])
435
+
436
+ # TODO Tab切り替えで、アノテ済みの一覧を表示する
437
+ # with gr.Tab("アノテ済みデータセット(管理画面)"):
438
+ # タブを切り替えた時にデータ表示を更新する
439
+
440
+ if __name__ == "__main__":
441
+ demo.launch()
note.txt CHANGED
@@ -5,4 +5,4 @@ Secretsに作成したTokenを
5
  HF_TOKENに設定して頂けますでしょうか?
6
 
7
  - team-hatakeyama-phase2/annotation_tanuki_phase2
8
- 側も設定が必要?
 
5
  HF_TOKENに設定して頂けますでしょうか?
6
 
7
  - team-hatakeyama-phase2/annotation_tanuki_phase2
8
+ 側も設定が必要?
requirements.txt CHANGED
@@ -1,4 +1,3 @@
1
  huggingface_hub==0.22.2
2
- minijinja
3
- transformers
4
  datasets
 
1
  huggingface_hub==0.22.2
2
+ gradio
 
3
  datasets