openfree commited on
Commit
fd896e6
·
verified ·
1 Parent(s): 8cbcecb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -132
app.py CHANGED
@@ -2,22 +2,21 @@ import gradio as gr
2
  import spaces
3
  import torch
4
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
5
- import torch.nn.functional as F
6
- import torch.nn as nn
7
  import re
8
  import requests
9
  from urllib.parse import urlparse
10
  import xml.etree.ElementTree as ET
11
 
 
12
  model_path = r'ssocean/NAIP'
13
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
14
 
15
- global model, tokenizer
16
  model = None
17
  tokenizer = None
18
 
19
  def fetch_arxiv_paper(arxiv_input):
20
- """Fetch paper details from arXiv URL or ID using requests."""
21
  try:
22
  if 'arxiv.org' in arxiv_input:
23
  parsed = urlparse(arxiv_input)
@@ -25,67 +24,57 @@ def fetch_arxiv_paper(arxiv_input):
25
  else:
26
  arxiv_id = arxiv_input.strip()
27
  api_url = f'http://export.arxiv.org/api/query?id_list={arxiv_id}'
28
- response = requests.get(api_url)
29
- if response.status_code != 200:
30
- return {"title": "", "abstract": "", "success": False, "message": "Error fetching paper from arXiv API"}
31
- root = ET.fromstring(response.text)
32
- ns = {'arxiv': 'http://www.w3.org/2005/Atom'}
33
- entry = root.find('.//arxiv:entry', ns)
34
  if entry is None:
35
- return {"title": "", "abstract": "", "success": False, "message": "Paper not found"}
36
- title = entry.find('arxiv:title', ns).text.strip()
37
- abstract = entry.find('arxiv:summary', ns).text.strip()
38
- return {"title": title, "abstract": abstract, "success": True, "message": "Paper fetched successfully!"}
39
  except Exception as e:
40
- return {"title": "", "abstract": "", "success": False, "message": f"Error fetching paper: {e}"}
41
 
42
  @spaces.GPU(duration=60, enable_queue=True)
43
  def predict(title, abstract):
44
- title = title.replace("\n", " ").strip().replace("''", "'")
45
- abstract = abstract.replace("\n", " ").strip().replace("''", "'")
46
  global model, tokenizer
 
 
47
  if model is None:
48
- # 1) 전부 float32 로드
49
- try:
50
- model = AutoModelForSequenceClassification.from_pretrained(
51
- model_path,
52
- num_labels=1,
53
- device_map=None,
54
- torch_dtype=torch.float32,
55
- load_in_8bit=False,
56
- load_in_4bit=False,
57
- low_cpu_mem_usage=False
58
- )
59
- except Exception as e:
60
- print(f"첫 로딩 실패, 재시도: {e}")
61
- model = AutoModelForSequenceClassification.from_pretrained(
62
- model_path,
63
- num_labels=1,
64
- torch_dtype=torch.float32
65
- )
66
- # 2) device에 올려보기 (unsupported error 무시)
67
- try:
68
- model.to(device)
69
- except ValueError as e:
70
- print(f"model.to() 무시: {e}")
71
  tokenizer = AutoTokenizer.from_pretrained(model_path)
 
72
  model.eval()
73
 
 
74
  text = (
75
- f"Given a certain paper, Title: {title}\n"
76
- f"Abstract: {abstract}.\n"
77
- "Predict its normalized academic impact (between 0 and 1):"
 
78
  )
 
79
  try:
80
- inputs = tokenizer(text, return_tensors="pt")
81
  inputs = {k: v.to(device) for k, v in inputs.items()}
82
  with torch.no_grad():
83
  outputs = model(**inputs)
84
  prob = torch.sigmoid(outputs.logits).item()
85
- score = min(1.0, prob + 0.05)
86
  return round(score, 4)
87
  except Exception as e:
88
- print(f"Prediction error: {e}")
89
  return 0.0
90
 
91
  def get_grade_and_emoji(score):
@@ -99,40 +88,18 @@ def get_grade_and_emoji(score):
99
  if score >= 0.300: return "CC ✏️"
100
  return "C 📑"
101
 
102
- example_papers = [
103
- {
104
- "title": "Attention Is All You Need",
105
- "abstract": "The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train.",
106
- "score": 0.982,
107
- "note": "💫 Revolutionary paper that introduced the Transformer architecture, fundamentally changing NLP and deep learning."
108
- },
109
- {
110
- "title": "Language Models are Few-Shot Learners",
111
- "abstract": "Recent work has demonstrated substantial gains on many NLP tasks and benchmarks by pre-training on a large corpus of text followed by fine-tuning on a specific task. While typically task-agnostic in architecture, this method still requires task-specific fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans can generally perform a new language task from only a few examples or from simple instructions - something which current NLP systems still largely struggle to do. Here we show that scaling up language models greatly improves task-agnostic, few-shot performance, sometimes even reaching competitiveness with prior state-of-the-art fine-tuning approaches.",
112
- "score": 0.956,
113
- "note": "🚀 Groundbreaking GPT-3 paper that demonstrated the power of large language models."
114
- },
115
- {
116
- "title": "An Empirical Study of Neural Network Training Protocols",
117
- "abstract": "This paper presents a comparative analysis of different training protocols for neural networks across various architectures. We examine the effects of learning rate schedules, batch size selection, and optimization algorithms on model convergence and final performance. Our experiments span multiple datasets and model sizes, providing practical insights for deep learning practitioners.",
118
- "score": 0.623,
119
- "note": "📚 Solid research paper with useful findings but more limited scope and impact."
120
- }
121
- ]
122
-
123
  def validate_input(title, abstract):
124
- title = title.replace("\n", " ").strip().replace("''", "'")
125
- abstract = abstract.replace("\n", " ").strip().replace("''", "'")
126
  non_latin = re.compile(r'[^\u0000-\u007F]')
127
  if len(title.split()) < 3:
128
- return False, "The title must be at least 3 words long."
129
  if len(abstract.split()) < 50:
130
- return False, "The abstract must be at least 50 words long."
131
  if non_latin.search(title):
132
- return False, "Title에 영어 외 문자가 포함되어 있습니다."
133
  if non_latin.search(abstract):
134
- return False, "Abstract에 영어 외 문자가 포함되어 있습니다."
135
- return True, "Inputs are valid!"
136
 
137
  def update_button_status(title, abstract):
138
  valid, msg = validate_input(title, abstract)
@@ -142,12 +109,13 @@ def update_button_status(title, abstract):
142
 
143
  def process_arxiv_input(arxiv_input):
144
  if not arxiv_input.strip():
145
- return "", "", "Please enter an arXiv URL or ID"
146
- result = fetch_arxiv_paper(arxiv_input)
147
- if result["success"]:
148
- return result["title"], result["abstract"], result["message"]
149
- return "", "", result["message"]
150
 
 
151
  css = """
152
  .gradio-container {
153
  font-family: 'Arial', sans-serif;
@@ -216,53 +184,41 @@ css = """
216
  }
217
  """
218
 
 
219
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
220
  gr.Markdown(
221
  """
222
- # Papers Impact: AI-Powered Research Impact Predictor
223
  ## https://discord.gg/openfreeai
224
- """
225
- )
226
  gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space">
227
  <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space&countColor=%23263759" />
228
  </a>""")
229
 
230
  with gr.Row():
231
  with gr.Column(elem_classes="input-section"):
232
- with gr.Group(elem_classes="arxiv-input"):
233
- gr.Markdown("### 📑 Import from arXiv")
234
- arxiv_input = gr.Textbox(
235
- lines=1,
236
- placeholder="Enter arXiv URL or ID (e.g., 2501.09751)",
237
- label="arXiv Paper URL/ID",
238
- value="https://arxiv.org/pdf/2502.07316"
239
- )
240
- gr.Markdown("""
241
- <p class="arxiv-note">
242
- Click input field to use example paper or browse papers at
243
- <a href="https://arxiv.org" target="_blank" class="arxiv-link">arxiv.org</a>
244
- </p>
245
- """)
246
- fetch_button = gr.Button("🔍 Fetch Paper Details", variant="secondary")
247
-
248
- gr.Markdown("### 📝 Or Enter Paper Details Manually")
249
  title_input = gr.Textbox(
250
  lines=2,
251
- placeholder="Enter Paper Title (minimum 3 words)...",
252
- label="Paper Title"
253
- )
254
  abstract_input = gr.Textbox(
255
  lines=5,
256
- placeholder="Enter Paper Abstract (minimum 50 words)...",
257
- label="Paper Abstract"
258
- )
259
- validation_status = gr.Textbox(label="✔️ Validation Status", interactive=False)
260
- submit_button = gr.Button("🎯 Predict Impact", interactive=False, variant="primary")
261
-
262
  with gr.Column(elem_classes="result-section"):
263
- with gr.Group():
264
- score_output = gr.Number(label="🎯 Impact Score")
265
- grade_output = gr.Textbox(label="🏆 Grade", elem_classes="grade-display")
266
 
267
  with gr.Row(elem_classes="methodology-section"):
268
  gr.Markdown(
@@ -283,44 +239,60 @@ with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
283
  | Grade | Score Range | Description | Indicator |
284
  |-------|-------------|-------------|-----------|
285
  | AAA | 0.900-1.000 | Exceptional Impact | 🌟 |
286
- | AA | 0.800-0.899 | Very High Impact | ⭐ |
287
- | A | 0.650-0.799 | High Impact | ✨ |
288
- | BBB | 0.600-0.649 | Above Average Impact | 🔵 |
289
- | BB | 0.550-0.599 | Moderate Impact | 📘 |
290
- | B | 0.500-0.549 | Average Impact | 📖 |
291
- | CCC | 0.400-0.499 | Below Average Impact | 📝 |
292
- | CC | 0.300-0.399 | Low Impact | ✏️ |
293
- | C | < 0.299 | Limited Impact | 📑 |
294
  """
295
  )
296
 
297
  with gr.Row(elem_classes="example-section"):
298
  gr.Markdown("### 📋 Example Papers")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  for paper in example_papers:
300
  gr.Markdown(
301
  f"""
302
  #### {paper['title']}
303
- **Score**: {paper.get('score', 'N/A')} | **Grade**: {get_grade_and_emoji(paper.get('score', 0))}
304
  {paper['abstract']}
305
  *{paper['note']}*
306
  ---
307
  """
308
  )
309
 
310
- title_input.change(update_button_status, [title_input, abstract_input], [validation_status, submit_button])
311
- abstract_input.change(update_button_status, [title_input, abstract_input], [validation_status, submit_button])
312
- fetch_button.click(process_arxiv_input, [arxiv_input], [title_input, abstract_input, validation_status])
 
313
 
314
- def process_prediction(title, abstract):
315
- score = predict(title, abstract)
316
- grade = get_grade_and_emoji(score)
317
- return score, grade
318
 
319
- submit_button.click(
320
- process_prediction,
321
- inputs=[title_input, abstract_input],
322
- outputs=[score_output, grade_output]
323
- )
324
 
325
  if __name__ == "__main__":
326
  iface.launch()
 
2
  import spaces
3
  import torch
4
  from transformers import AutoTokenizer, AutoModelForSequenceClassification
 
 
5
  import re
6
  import requests
7
  from urllib.parse import urlparse
8
  import xml.etree.ElementTree as ET
9
 
10
+ # 모델 경로와 디바이스 설정
11
  model_path = r'ssocean/NAIP'
12
  device = 'cuda' if torch.cuda.is_available() else 'cpu'
13
 
14
+ # 전역 변수로 모델·토크나이저 선언
15
  model = None
16
  tokenizer = None
17
 
18
  def fetch_arxiv_paper(arxiv_input):
19
+ """arXiv URL 또는 ID로부터 제목과 요약(fetch)"""
20
  try:
21
  if 'arxiv.org' in arxiv_input:
22
  parsed = urlparse(arxiv_input)
 
24
  else:
25
  arxiv_id = arxiv_input.strip()
26
  api_url = f'http://export.arxiv.org/api/query?id_list={arxiv_id}'
27
+ resp = requests.get(api_url)
28
+ if resp.status_code != 200:
29
+ return {"title":"", "abstract":"", "success":False, "message":"arXiv API 에러"}
30
+ root = ET.fromstring(resp.text)
31
+ ns = {'atom': 'http://www.w3.org/2005/Atom'}
32
+ entry = root.find('.//atom:entry', ns)
33
  if entry is None:
34
+ return {"title":"", "abstract":"", "success":False, "message":"논문을 찾을 수 없습니다"}
35
+ title = entry.find('atom:title', ns).text.strip()
36
+ abstract = entry.find('atom:summary', ns).text.strip()
37
+ return {"title": title, "abstract": abstract, "success": True, "message": "성공적으로 가져왔습니다!"}
38
  except Exception as e:
39
+ return {"title":"", "abstract":"", "success":False, "message":f"오류: {e}"}
40
 
41
  @spaces.GPU(duration=60, enable_queue=True)
42
  def predict(title, abstract):
43
+ """논문 제목과 요약을 받아 0~1 사이의 impact score 예측"""
 
44
  global model, tokenizer
45
+
46
+ # 최초 호출 시 모델·토크나이저 로드
47
  if model is None:
48
+ model = AutoModelForSequenceClassification.from_pretrained(
49
+ model_path,
50
+ num_labels=1,
51
+ quantization_config=None, # bitsandbytes 양자화 완전 비활성화
52
+ torch_dtype=torch.float32, # 전부 float32
53
+ device_map=None, # accelerate dispatch 비활성
54
+ low_cpu_mem_usage=False
55
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  tokenizer = AutoTokenizer.from_pretrained(model_path)
57
+ model.to(device)
58
  model.eval()
59
 
60
+ # 입력 텍스트 구성
61
  text = (
62
+ f"Given a certain paper,\n"
63
+ f"Title: {title.strip()}\n"
64
+ f"Abstract: {abstract.strip()}\n"
65
+ f"Predict its normalized academic impact (0~1):"
66
  )
67
+
68
  try:
69
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=1024)
70
  inputs = {k: v.to(device) for k, v in inputs.items()}
71
  with torch.no_grad():
72
  outputs = model(**inputs)
73
  prob = torch.sigmoid(outputs.logits).item()
74
+ score = min(1.0, prob + 0.05) # +0.05 보정, 최대 1.0
75
  return round(score, 4)
76
  except Exception as e:
77
+ print("Prediction error:", e)
78
  return 0.0
79
 
80
  def get_grade_and_emoji(score):
 
88
  if score >= 0.300: return "CC ✏️"
89
  return "C 📑"
90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  def validate_input(title, abstract):
92
+ """제목·요약 글자 수 및 비영어 문자 검사"""
 
93
  non_latin = re.compile(r'[^\u0000-\u007F]')
94
  if len(title.split()) < 3:
95
+ return False, "제목은 최소 3단어 이상이어야 합니다."
96
  if len(abstract.split()) < 50:
97
+ return False, "요약은 최소 50단어 이상이어야 합니다."
98
  if non_latin.search(title):
99
+ return False, "제목에 영어 외 문자가 포함되었습니다."
100
  if non_latin.search(abstract):
101
+ return False, "요약에 영어 외 문자가 포함되었습니다."
102
+ return True, "입력 유효합니다."
103
 
104
  def update_button_status(title, abstract):
105
  valid, msg = validate_input(title, abstract)
 
109
 
110
  def process_arxiv_input(arxiv_input):
111
  if not arxiv_input.strip():
112
+ return "", "", "URL 또는 ID를 입력하세요"
113
+ res = fetch_arxiv_paper(arxiv_input)
114
+ if res["success"]:
115
+ return res["title"], res["abstract"], res["message"]
116
+ return "", "", res["message"]
117
 
118
+ # CSS 정의
119
  css = """
120
  .gradio-container {
121
  font-family: 'Arial', sans-serif;
 
184
  }
185
  """
186
 
187
+ # Gradio UI 구성
188
  with gr.Blocks(theme=gr.themes.Default(), css=css) as iface:
189
  gr.Markdown(
190
  """
191
+ # Papers Impact: AI-Powered Research Impact Predictor
192
  ## https://discord.gg/openfreeai
193
+ """)
 
194
  gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space">
195
  <img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2FVIDraft-PaperImpact.hf.space&countColor=%23263759" />
196
  </a>""")
197
 
198
  with gr.Row():
199
  with gr.Column(elem_classes="input-section"):
200
+ gr.Markdown("### 📑 arXiv에서 불러오기")
201
+ arxiv_input = gr.Textbox(
202
+ lines=1,
203
+ placeholder="arXiv URL 또는 ID",
204
+ label="arXiv URL/ID")
205
+ fetch_btn = gr.Button("🔍 불러오기", variant="secondary")
206
+
207
+ gr.Markdown("### 📝 직접 입력")
 
 
 
 
 
 
 
 
 
208
  title_input = gr.Textbox(
209
  lines=2,
210
+ placeholder="논문 제목 (최소 3단어)",
211
+ label="제목")
 
212
  abstract_input = gr.Textbox(
213
  lines=5,
214
+ placeholder="논문 요약 (최소 50단어)",
215
+ label="요약")
216
+ status = gr.Textbox(label="✔️ 입력 상태", interactive=False)
217
+ submit_btn = gr.Button("🎯 예측하기", interactive=False, variant="primary")
218
+
 
219
  with gr.Column(elem_classes="result-section"):
220
+ score_out = gr.Number(label="🎯 Impact Score")
221
+ grade_out = gr.Textbox(label="🏆 Grade", elem_classes="grade-display")
 
222
 
223
  with gr.Row(elem_classes="methodology-section"):
224
  gr.Markdown(
 
239
  | Grade | Score Range | Description | Indicator |
240
  |-------|-------------|-------------|-----------|
241
  | AAA | 0.900-1.000 | Exceptional Impact | 🌟 |
242
+ | AA | 0.800-0.899 | Very High Impact | ⭐ |
243
+ | A | 0.650-0.799 | High Impact | ✨ |
244
+ | BBB | 0.600-0.649 | Above Average | 🔵 |
245
+ | BB | 0.550-0.599 | Moderate Impact | 📘 |
246
+ | B | 0.500-0.549 | Average Impact | 📖 |
247
+ | CCC | 0.400-0.499 | Below Average | 📝 |
248
+ | CC | 0.300-0.399 | Low Impact | ✏️ |
249
+ | C | <0.299 | Limited Impact | 📑 |
250
  """
251
  )
252
 
253
  with gr.Row(elem_classes="example-section"):
254
  gr.Markdown("### 📋 Example Papers")
255
+ example_papers = [
256
+ {
257
+ "title": "Attention Is All You Need",
258
+ "abstract": "The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder. The best performing models also connect the encoder and decoder through an attention mechanism. We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence and convolutions entirely. Experiments on two machine translation tasks show these models to be superior in quality while being more parallelizable and requiring significantly less time to train.",
259
+ "score": 0.982,
260
+ "note": "💫 Revolutionary paper that introduced the Transformer architecture, fundamentally changing NLP and deep learning."
261
+ },
262
+ {
263
+ "title": "Language Models are Few-Shot Learners",
264
+ "abstract": "Recent work has demonstrated substantial gains on many NLP tasks and benchmarks by pre-training on a large corpus of text followed by fine-tuning on a specific task. While typically task-agnostic in architecture, this method still requires task-specific fine-tuning datasets of thousands or tens of thousands of examples. By contrast, humans can generally perform a new language task from only a few examples or from simple instructions - something which current NLP systems still largely struggle to do. Here we show that scaling up language models greatly improves task-agnostic, few-shot performance, sometimes even reaching competitiveness with prior state-of-the-art fine-tuning approaches.",
265
+ "score": 0.956,
266
+ "note": "🚀 Groundbreaking GPT-3 paper that demonstrated the power of large language models."
267
+ },
268
+ {
269
+ "title": "An Empirical Study of Neural Network Training Protocols",
270
+ "abstract": "This paper presents a comparative analysis of different training protocols for neural networks across various architectures. We examine the effects of learning rate schedules, batch size selection, and optimization algorithms on model convergence and final performance. Our experiments span multiple datasets and model sizes, providing practical insights for deep learning practitioners.",
271
+ "score": 0.623,
272
+ "note": "📚 Solid research paper with useful findings but more limited scope and impact."
273
+ }
274
+ ]
275
  for paper in example_papers:
276
  gr.Markdown(
277
  f"""
278
  #### {paper['title']}
279
+ **Score**: {paper['score']} | **Grade**: {get_grade_and_emoji(paper['score'])}
280
  {paper['abstract']}
281
  *{paper['note']}*
282
  ---
283
  """
284
  )
285
 
286
+ # 이벤트 핸들러 연결
287
+ title_input.change(update_button_status, [title_input, abstract_input], [status, submit_btn])
288
+ abstract_input.change(update_button_status, [title_input, abstract_input], [status, submit_btn])
289
+ fetch_btn.click(process_arxiv_input, [arxiv_input], [title_input, abstract_input, status])
290
 
291
+ def run_predict(t, a):
292
+ s = predict(t, a)
293
+ return s, get_grade_and_emoji(s)
 
294
 
295
+ submit_btn.click(run_predict, [title_input, abstract_input], [score_out, grade_out])
 
 
 
 
296
 
297
  if __name__ == "__main__":
298
  iface.launch()