File size: 15,852 Bytes
57a9ebf
470f648
57a9ebf
 
984938f
57a9ebf
67aea18
984938f
a749c57
57a9ebf
984938f
 
 
9646494
2ef4dc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530316e
984938f
 
 
 
 
 
 
fee908c
984938f
 
57a9ebf
984938f
 
57a9ebf
984938f
47e27e9
 
 
 
 
 
984938f
 
 
 
 
 
 
 
 
 
13ac977
984938f
13ac977
 
fee908c
984938f
 
 
 
 
 
 
 
ad8a799
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f6b8d98
 
ad8a799
 
 
 
 
 
 
f6b8d98
ad8a799
 
 
 
 
 
 
 
 
 
 
 
f6b8d98
ad8a799
 
 
 
 
 
 
f6b8d98
ad8a799
 
984938f
 
f6b8d98
 
 
 
 
 
 
 
984938f
f6b8d98
47e27e9
 
 
 
2ef4dc2
47e27e9
4fe6183
984938f
 
 
4fe6183
984938f
 
 
530316e
984938f
 
 
b5e9c50
 
 
 
 
984938f
2ef4dc2
ad8a799
b5e9c50
47e27e9
984938f
2ef4dc2
ad8a799
b5e9c50
47e27e9
2ef4dc2
 
 
 
984938f
2ef4dc2
 
 
 
984938f
2ef4dc2
 
530316e
1fd1dd9
984938f
 
c2171bf
1fd1dd9
c2171bf
 
984938f
c2171bf
1fd1dd9
fb9334a
1fd1dd9
2ef4dc2
 
fa3936e
 
 
 
984938f
1fd1dd9
 
fb9334a
984938f
1fd1dd9
 
 
 
 
984938f
530316e
984938f
8f72d39
984938f
8f72d39
 
984938f
 
8f72d39
 
984938f
 
 
 
 
 
 
 
 
d771f77
 
ad8a799
8f72d39
 
 
 
 
 
 
 
ad8a799
 
 
 
d771f77
984938f
d771f77
 
984938f
7a6488d
 
984938f
 
d771f77
 
984938f
d771f77
 
 
 
2ef4dc2
d771f77
 
222f125
 
984938f
d771f77
47e27e9
 
 
 
 
 
 
 
012edbb
ad8a799
 
51eb0c1
011ee26
ad8a799
8f72d39
ad8a799
66a05f8
012edbb
d771f77
222f125
 
 
 
 
 
 
 
 
 
 
 
 
984938f
222f125
 
 
 
 
 
 
012edbb
222f125
984938f
57a9ebf
984938f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import gradio as gr
import spaces
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
from peft import AutoPeftModelForSequenceClassification
import torch.nn.functional as F
import re

device = 'cuda:0'

# ===== v1 部分 =====
model_v1, tokenizer_v1 = None, None
model_path_v1 = r'ssocean/NAIP'

@spaces.GPU(duration=60, enable_queue=True)
# def predict_v1(title, abstract):
#     global model_v1, tokenizer_v1
#     if model_v1 is None:
#         model_v1 = AutoModelForSequenceClassification.from_pretrained(
#             model_path_v1,
#             num_labels=1,
#             load_in_8bit=True,
#         ).eval()
#         tokenizer_v1 = AutoTokenizer.from_pretrained(model_path_v1)
#         model_v1.eval()

#     text = f"Given a certain paper, Title: {title}\n Abstract: {abstract}. \n Predict its normalized academic impact (between 0 and 1):"
#     inputs = tokenizer_v1(text, return_tensors="pt").to(device)
#     with torch.no_grad():
#         outputs = model_v1(**inputs)
#     prob = torch.sigmoid(outputs.logits).item()
#     if prob + 0.05 >= 1.0:
#         return round(1, 4)
#     return round(prob + 0.05, 4) 
@spaces.GPU(duration=60, enable_queue=True)
def predict_v1(title, abstract):
    global model_v1, tokenizer_v1
    if model_v1 is None:
        model_v1 = AutoModelForSequenceClassification.from_pretrained(
            model_path_v1,
            num_labels=1,
            load_in_8bit=True,
        ).eval()
        tokenizer_v1 = AutoTokenizer.from_pretrained(model_path_v1)
        model_v1.eval()

    text = f"Given a certain paper, Title: {title}\n Abstract: {abstract}. \n Predict its normalized academic impact (between 0 and 1):"
    inputs = tokenizer_v1(text, return_tensors="pt").to(device)
    with torch.no_grad():
        outputs = model_v1(**inputs)
    
    raw_score = outputs.logits.item()                  # 原始绝对数值 (logit)
    final_score = torch.sigmoid(outputs.logits).item() # 概率
    final_score = min(1.0, final_score + 0.05)         # 你的归一化规则
    
    return round(raw_score, 4), round(final_score, 4)
# ===== v2 部分 =====
scorer_v2 = None
model_path_v2 = r'ssocean/NAIPv2'

class PaperScorer:
    def __init__(self, model_path: str, device: str = 'cuda', max_length: int = 512):
        self.device = torch.device(device if torch.cuda.is_available() else 'cpu')
        self.max_length = max_length

        # PEFT 模型 (LoRA)
        self.model = AutoModelForSequenceClassification.from_pretrained(
            model_path,
            num_labels=1,
            load_in_8bit=True,
        ).eval()

        self.tokenizer = AutoTokenizer.from_pretrained(model_path)
        self.tokenizer.pad_token = self.tokenizer.eos_token
        self.model.config.pad_token_id = self.tokenizer.pad_token_id

        self.prompt_template = (
            "Given a certain paper, Title: {title}\nAbstract: {abstract}\nEvaluate the quality of this paper:"
        )
    # def _rescale_score(self, s: float) -> float:
    #     """
    #     Piecewise linear rescaling:
    #       0.0 ~ 0.2  ->  0.0 ~ 0.5
    #       0.2 ~ 0.6  ->  0.5 ~ 1.0
    #       >0.6       ->  1.0
    #     """
    #     s = max(0.0, min(1.0, s))  # clamp input to [0,1]
    
    #     if s <= 0.2:
    #         # scale [0,0.2] to [0,0.5]
    #         y = (s / 0.2) * 0.5
    #     elif s <= 0.6:
    #         # scale (0.2,0.6] to (0.5,1.0]
    #         y = 0.5 + ((s - 0.2) / 0.4) * 0.5
    #     else:
    #         # everything above 0.6 maps to 1.0
    #         y = 1.0
    
    #     return round(y, 4)
    def _rescale_score(self, s: float) -> float:
        """
        Piecewise linear rescaling with custom anchors.
        Mapping examples:
          0.233 -> 0.465
          0.372 -> 0.608
          0.423 -> 0.714
          0.496 -> 0.786
        Entire range still in [0,1].
        """
        # clamp input
        s = max(0.0, min(1.0, s))
    
        # define anchors (x -> y)
        anchors = [
            (0.0,   0.0),
            (0.233, 0.465),
            (0.372, 0.608),
            (0.423, 0.714),
            (0.496, 0.786),
            (1.0,   1.0),
        ]
    
        # find interval
        for (x1, y1), (x2, y2) in zip(anchors, anchors[1:]):
            if x1 <= s <= x2:
                # linear interpolation
                t = (s - x1) / (x2 - x1)
                y = y1 + t * (y2 - y1)
                return round(y, 4)
    
        return 1.0

    def score(self, title: str, abstract: str) -> float:
        prompt = self.prompt_template.format(title=title.strip(), abstract=abstract.strip())
        inputs = self.tokenizer(
            prompt,
            return_tensors='pt',
            padding=True,
            truncation=True,
            max_length=self.max_length
        ).to(self.device)

        with torch.no_grad():
            logits = self.model(**inputs).logits
            raw_score = logits.view(-1).item()                  # 原始 logit
        
        sigmoid_score = torch.sigmoid(torch.tensor(raw_score)).item()  # 压缩到 0-1
        final_score = self._rescale_score(sigmoid_score)               # 在 sigmoid 基础上做 piecewise
        return round(raw_score, 4), round(final_score, 4)
        
@spaces.GPU(duration=60, enable_queue=True)
def predict_v2(title, abstract):
    global scorer_v2
    if scorer_v2 is None:
        scorer_v2 = PaperScorer(model_path_v2)
    return scorer_v2.score(title, abstract)



def predict(title, abstract, model_version):
    title = title.replace("\n", " ").strip().replace('’', "'")
    abstract = abstract.replace("\n", " ").strip().replace('’', "'")
    print(f'-------------------------------------------------------------------------------')
    print(f'Model Version: {model_version}')
    print(f'Title: {title}')
    print(f'Abstract: {abstract}')
    
    if model_version == "v1":
        raw, final = predict_v1(title, abstract)
        print(f'Raw Score: {raw}, Normalized Score: {final}')
        print(f'-------------------------------------------------------------------------------\n\n')
        return [["Raw Score", round(raw, 4)], ["Final Score", round(final, 4)]]
    else:
        raw, final = predict_v2(title, abstract)
        print(f'Raw Score: {raw}, Normalized Score: {final}')
        print(f'-------------------------------------------------------------------------------\n\n')
        return [["Raw Score", round(raw, 4)], ["Final Score", round(final, 4)]]

# def predict(title, abstract, model_version):
#     title = title.replace("\n", " ").strip().replace('’', "'")
#     abstract = abstract.replace("\n", " ").strip().replace('’', "'")

#     if model_version == "v1":
#         raw, final = predict_v1(title, abstract)
#     else:
#         raw, final = predict_v2(title, abstract)

#     print(f"Raw: {raw}, Final: {final}")
#     return {"Raw Score": raw, "Final Score": final}

def validate_input(title, abstract):
    title = title.replace("\n", " ").strip().replace('’', "'")
    abstract = abstract.replace("\n", " ").strip().replace('’', "'")

    non_latin_pattern = re.compile(r'[^\u0000-\u007F]')
    non_latin_in_title = non_latin_pattern.findall(title)
    non_latin_in_abstract = non_latin_pattern.findall(abstract)

    if len(title.strip().split(' ')) < 3:
        return False, "The title must be at least 3 words long."
    if len(abstract.strip().split(' ')) < 50:
        return False, "The abstract must be at least 50 words long."
    if len((title + abstract).split(' ')) > 600:
        return True, "Warning, the input length is approaching tokenization limits (512) and may be truncated without further warning!"
    if non_latin_in_title:
        return False, f"The title contains invalid characters: {', '.join(non_latin_in_title)}. Only English letters and special symbols are allowed."
    if non_latin_in_abstract:
        return False, f"The abstract contains invalid characters: {', '.join(non_latin_in_abstract)}. Only English letters and special symbols are allowed."

    return True, "Inputs are valid! Good to go!"


def update_button_status(title, abstract):
    valid, message = validate_input(title, abstract)
    if not valid:
        return gr.update(value="Error: " + message), gr.update(interactive=False)
    return gr.update(value=message), gr.update(interactive=True)



examples = [

    [
        "Mean Flows for One-step Generative Modeling",
        ('''We propose a principled and effective framework for one-step generative modeling. We introduce the notion of average velocity to characterize flow fields, in contrast to instantaneous velocity modeled by Flow Matching methods. A well-defined identity between average and instantaneous velocities is derived and used to guide neural network training. Our method, termed the MeanFlow model, is self-contained and requires no pre-training, distillation, or curriculum learning. MeanFlow demonstrates strong empirical performance: it achieves an FID of 3.43 with a single function evaluation (1-NFE) on ImageNet 256x256 trained from scratch, significantly outperforming previous state-of-the-art one-step diffusion/flow models. Our study substantially narrows the gap between one-step diffusion/flow models and their multi-step predecessors, and we hope it will motivate future research to revisit the foundations of these powerful models.''')
    ],
    [
        "SARDet-100K: Towards Open-Source Benchmark and ToolKit for Large-Scale SAR Object Detection",
        ('''Synthetic Aperture Radar (SAR) object detection has gained significant attention recently due to its irreplaceable all-weather imaging capabilities. However, this research field suffers from both limited public datasets (mostly comprising <2K images with only mono-category objects) and inaccessible source code. To tackle these challenges, we establish a new benchmark dataset and an open-source method for large-scale SAR object detection. Our dataset, SARDet-100K, is a result of intense surveying, collecting, and standardizing 10 existing SAR detection datasets, providing a large-scale and diverse dataset for research purposes. To the best of our knowledge, SARDet-100K is the first COCO-level large-scale multi-class SAR object detection dataset ever created. With this high-quality dataset, we conducted comprehensive experiments and uncovered a crucial challenge in SAR object detection: the substantial disparities between the pretraining on RGB datasets and finetuning on SAR datasets in terms of both data domain and model structure. To bridge these gaps, we propose a novel Multi-Stage with Filter Augmentation (MSFA) pretraining framework that tackles the problems from the perspective of data input, domain transition, and model migration. The proposed MSFA method significantly enhances the performance of SAR object detection models while demonstrating exceptional generalizability and flexibility across diverse models. This work aims to pave the way for further advancements in SAR object detection. The dataset and code is available at this https URL.''')
    ],
    [
        "Enhanced ZSSR for Super-resolution Reconstruction of the Historical Tibetan Document Images",
        "Due to the poor preservation and imaging conditions, the image quality of historical Tibetan document images is relatively unsatisfactory. In this paper, we adopt super-resolution technology to reconstruct high quality images of historical Tibetan document. To address the problem of low quantity and poor quality of historical Tibetan document images, we propose the EZSSR network based on the Zero-Shot Super-resolution Network (ZSSR), which borrows the idea of feature pyramid in Deep Laplacian Pyramid Networks (LapSRN) to extract different levels of features while alleviating the ringing artifacts. EZSSR neither requires paired training datasets nor preprocessing stage. The computational complexity of EZSSR is low, and thus, EZSSR can also reconstruct image within the acceptable time frame. Experimental results show that EZSSR reconstructs images with better visual effects and higher PSNR and SSIM values."
    ]
    
]

# ===== Gradio 界面 =====
with gr.Blocks() as iface:
    gr.Markdown("""
    # 📈 Predict Impact & Quality of Newborn Papers
    
    ### LLM-powered estimates from a paper’s title and abstract.
    
    #### Which model should I use?
    - [**NAIPv1**](https://arxiv.org/abs/2408.03934) — predicts **academic impact**  
    - [**NAIPv2**](https://arxiv.org/abs/2509.25179) — predicts **paper quality** *(default)*  
    *See the papers for methodology and evaluation details.*
    
    > ⚡ **Note:** Local inference is instantaneous. On **Hugging Face ZeroGPU**, the quantized model is reloaded **from disk** on each prediction, which can introduce significant disk-I/O delays (typically <30 s).

    > For **NAIPv2**, the output **Normalized score** may not be comparable across different domains.  It is recommended to use the **Raw score** magnitude for quality estimation within the same domain.

    """)

    with gr.Row():
        with gr.Column():
            model_selector = gr.Dropdown(
                choices=["v1", "v2.1"],
                value="v2.1",   # 默认 v2
                label="Select Model Version"
            )
            title_input = gr.Textbox(
                lines=2,
                placeholder="Enter Paper Title Here...",
                label="Paper Title"
            )
            abstract_input = gr.Textbox(
                lines=5,
                placeholder="Enter Paper Abstract Here... (Tip: For v2, remove sentences like 'Our code is released at xxx' for more accurate results)",
                label="Paper Abstract"
            )
            validation_status = gr.Textbox(label="Validation Status", interactive=False)
            submit_button = gr.Button("Predict Impact", interactive=False)

        with gr.Column():
            output = gr.Dataframe(
                headers=["Type", "Score"],
                datatype=["str", "number"],
                row_count=2,
                col_count=2,
                interactive=False,
                label="Predicted Scores"
            )
            gr.Markdown("""
            ## Important Notes 
            - The reported performance reflects aggregate statistical outcomes, rather than guarantees for individual instances.  
            - It is intended as a tool **for research and educational purposes only**.
            - Please refrain from deliberately embellishing the title and abstract to boost scores, and **avoid making false claims**.
            - This demo is an early exploration of using LLMs for paper quality estimation and is not optimized against prompt injection attacks.
            - The **predicted value** is a probability generated by the model and **does NOT reflect the paper's true quality or novelty**.  
            - For **NAIPv1**, a normalized score greater than **0.60** is considered to indicate a potentially impactful paper.  
            - For **NAIPv2**, a normalized score above **0.60** corresponds to the statistical mean of NeurIPS accepted papers (Poster).  
            """)

    title_input.change(
        update_button_status,
        inputs=[title_input, abstract_input],
        outputs=[validation_status, submit_button]
    )
    abstract_input.change(
        update_button_status,
        inputs=[title_input, abstract_input],
        outputs=[validation_status, submit_button]
    )

    submit_button.click(
        predict,
        inputs=[title_input, abstract_input, model_selector],
        outputs=output
    )

    gr.Examples(
        examples=examples,
        inputs=[title_input, abstract_input],
        outputs=[validation_status, output],
        cache_examples=False
    )

iface.launch()