sepp81 commited on
Commit
279a22c
·
verified ·
1 Parent(s): 6de7b7f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -51
app.py CHANGED
@@ -11,7 +11,7 @@ def local_css():
11
  st.markdown("""
12
  <style>
13
  /* Global Styling */
14
-
15
  /* Game Container */
16
  .game-container {
17
  border-radius: 15px;
@@ -21,12 +21,12 @@ def local_css():
21
  }
22
  /* Sentences Styling */
23
  .sentence-container {
 
 
24
  border-left: 4px solid #3498db;
25
  padding: 10px;
26
  margin-bottom: 10px;
27
- display: flex;
28
- align-items: center;
29
- justify-content: space-between;
30
  }
31
  .sentence-container:hover {
32
  transform: translateX(5px);
@@ -63,6 +63,15 @@ def local_css():
63
  background-color: #2c3e50;
64
  color: white;
65
  }
 
 
 
 
 
 
 
 
 
66
  </style>
67
  """, unsafe_allow_html=True)
68
 
@@ -70,7 +79,7 @@ class RoFTGame:
70
  def __init__(self, dataset_path):
71
  """
72
  Initialize the RoFT Game with the dataset
73
-
74
  :param dataset_path: Path to the roft.csv file
75
  """
76
  self.df = pd.read_csv(dataset_path)
@@ -78,14 +87,15 @@ class RoFTGame:
78
  self.current_sentences = None
79
  self.true_boundary_index = None
80
  self.current_guess_index = None
 
81
  self.predefined_reasons = [
82
  "grammar",
83
- "repetition",
84
- "irrelevant",
85
- "contradicts_sentence",
86
- "contradicts_knowledge",
87
- "common_sense",
88
- "coreference",
89
  "generic"
90
  ]
91
 
@@ -93,32 +103,46 @@ class RoFTGame:
93
  """
94
  Load a random sample from the dataset
95
  """
 
96
  valid_samples = self.df[
97
- (self.df['gen_body'].notna()) &
98
- (self.df['reason'].notna()) &
99
  (self.df['reason'] != '[]')
100
  ]
101
-
 
102
  self.current_sample = valid_samples.sample(n=1).iloc[0]
103
-
 
104
  prompt_sentences = self.current_sample['prompt_body'].split('_SEP_')
105
  gen_sentences = self.current_sample['gen_body'].split('_SEP_')
106
 
 
107
  combined_sentences = prompt_sentences + gen_sentences
108
  self.current_sentences = combined_sentences[:10]
109
-
 
110
  self.true_boundary_index = self.current_sample['true_boundary_index']
111
-
 
112
  try:
113
  self.current_reasons = ast.literal_eval(self.current_sample['reason'])
114
  except:
115
  self.current_reasons = []
116
-
 
117
  self.current_guess_index = None
118
 
119
  def check_guess(self, guess_index):
 
 
 
 
 
 
120
  self.current_guess_index = guess_index
121
-
 
122
  if guess_index == self.true_boundary_index:
123
  return 5
124
  elif guess_index > self.true_boundary_index:
@@ -126,80 +150,187 @@ class RoFTGame:
126
  else:
127
  return 0
128
 
129
- def save_annotation(self, guess_index, reason):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  os.makedirs('logs', exist_ok=True)
 
 
131
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
132
  filename = f'logs/annotation_{timestamp}.txt'
133
-
 
134
  annotation_details = [
135
  f"Timestamp: {timestamp}",
136
  f"Model: {self.current_sample['model']}",
137
  f"Dataset: {self.current_sample['dataset']}",
138
  f"Guess Index: {guess_index + 1}",
139
  f"True Boundary Index: {self.true_boundary_index + 1}",
140
- f"User Reason: {reason}"
 
 
 
 
141
  ]
142
-
 
143
  with open(filename, 'w') as f:
144
  f.write("\n".join(annotation_details))
145
 
146
  def main():
147
  local_css()
148
-
 
149
  st.markdown("""
150
- <h1 style='text-align: center; color: #2c3e50;'>
 
 
151
  🕵️ Real or Fake Text Detective 🕵️‍♀️
152
  </h1>
153
  """, unsafe_allow_html=True)
154
 
 
 
 
 
 
 
 
 
 
 
 
155
  if 'game' not in st.session_state:
156
  st.session_state.game = RoFTGame('roft.csv')
157
  st.session_state.game.load_random_sample()
158
  st.session_state.total_points = 0
159
  st.session_state.rounds_played = 0
160
-
 
 
 
 
161
  st.sidebar.markdown("## 🎮 Game Stats")
162
  st.sidebar.markdown(f"### 🏆 Total Points: {st.session_state.total_points}")
163
  st.sidebar.markdown(f"### 🎲 Rounds Played: {st.session_state.rounds_played}")
164
 
165
- st.markdown("<div class='game-container'>", unsafe_allow_html=True)
166
-
 
 
 
 
 
 
 
 
 
167
  st.subheader("🔍 Examine the Text Carefully")
168
- guess = None
169
- for i, sentence in enumerate(st.session_state.game.current_sentences):
170
- col1, col2 = st.columns([9, 1])
171
- with col1:
172
- st.markdown(f"<div class='sentence-container'><strong>{i+1}.</strong> {sentence}</div>", unsafe_allow_html=True)
173
- with col2:
174
- if st.radio("", ["", str(i + 1)], key=f"guess_{i}"):
175
- guess = i
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
 
 
 
177
  if st.button("Submit Guess"):
178
- if guess is None:
179
- st.warning("Please select where the AI-generated text begins.")
180
  else:
181
- points_earned = st.session_state.game.check_guess(guess)
 
 
 
 
182
  st.session_state.total_points += points_earned
183
  st.session_state.rounds_played += 1
 
 
 
 
 
 
 
 
184
 
185
- st.subheader("Results")
186
- st.write(f"Your Guess: Sentence {guess + 1}")
187
- st.write(f"Actual Boundary: Sentence {st.session_state.game.true_boundary_index + 1}")
188
- st.write(f"Points Earned: {points_earned}")
 
189
 
190
- if points_earned > 0:
191
- reason = st.text_area("Explain Your Reason")
192
- if reason:
193
- st.session_state.game.save_annotation(guess, reason)
194
- st.success("Reason saved successfully.")
195
  else:
196
- st.info("You didn't get the boundary right. Try again in the next round!")
197
-
 
 
 
 
198
  if st.button("Next Round"):
199
  st.session_state.game.load_random_sample()
200
  st.experimental_rerun()
201
-
202
  st.markdown("</div>", unsafe_allow_html=True)
203
 
 
 
 
 
 
 
 
 
 
204
  if __name__ == "__main__":
205
  main()
 
11
  st.markdown("""
12
  <style>
13
  /* Global Styling */
14
+
15
  /* Game Container */
16
  .game-container {
17
  border-radius: 15px;
 
21
  }
22
  /* Sentences Styling */
23
  .sentence-container {
24
+ display: flex;
25
+ align-items: center;
26
  border-left: 4px solid #3498db;
27
  padding: 10px;
28
  margin-bottom: 10px;
29
+ transition: all 0.3s ease;
 
 
30
  }
31
  .sentence-container:hover {
32
  transform: translateX(5px);
 
63
  background-color: #2c3e50;
64
  color: white;
65
  }
66
+ /* Reason Validation */
67
+ .reason-valid {
68
+ color: #2ecc71;
69
+ font-weight: bold;
70
+ }
71
+ .reason-invalid {
72
+ color: #e74c3c;
73
+ font-weight: bold;
74
+ }
75
  </style>
76
  """, unsafe_allow_html=True)
77
 
 
79
  def __init__(self, dataset_path):
80
  """
81
  Initialize the RoFT Game with the dataset
82
+
83
  :param dataset_path: Path to the roft.csv file
84
  """
85
  self.df = pd.read_csv(dataset_path)
 
87
  self.current_sentences = None
88
  self.true_boundary_index = None
89
  self.current_guess_index = None
90
+ # Predefined reasons from the dataset description
91
  self.predefined_reasons = [
92
  "grammar",
93
+ "repetition",
94
+ "irrelevant",
95
+ "contradicts_sentence",
96
+ "contradicts_knowledge",
97
+ "common_sense",
98
+ "coreference",
99
  "generic"
100
  ]
101
 
 
103
  """
104
  Load a random sample from the dataset
105
  """
106
+ # Filter for samples with valid generations and reasons
107
  valid_samples = self.df[
108
+ (self.df['gen_body'].notna()) &
109
+ (self.df['reason'].notna()) &
110
  (self.df['reason'] != '[]')
111
  ]
112
+
113
+ # Select a random sample
114
  self.current_sample = valid_samples.sample(n=1).iloc[0]
115
+
116
+ # Prepare sentences
117
  prompt_sentences = self.current_sample['prompt_body'].split('_SEP_')
118
  gen_sentences = self.current_sample['gen_body'].split('_SEP_')
119
 
120
+ # Combine and truncate to 10 sentences
121
  combined_sentences = prompt_sentences + gen_sentences
122
  self.current_sentences = combined_sentences[:10]
123
+
124
+ # Store true boundary
125
  self.true_boundary_index = self.current_sample['true_boundary_index']
126
+
127
+ # Parse reasons from the dataset
128
  try:
129
  self.current_reasons = ast.literal_eval(self.current_sample['reason'])
130
  except:
131
  self.current_reasons = []
132
+
133
+ # Reset current guess
134
  self.current_guess_index = None
135
 
136
  def check_guess(self, guess_index):
137
+ """
138
+ Check if the guess is correct
139
+
140
+ :param guess_index: Index of the guessed boundary
141
+ :return: Points earned
142
+ """
143
  self.current_guess_index = guess_index
144
+
145
+ # Calculate points based on closeness to true boundary
146
  if guess_index == self.true_boundary_index:
147
  return 5
148
  elif guess_index > self.true_boundary_index:
 
150
  else:
151
  return 0
152
 
153
+ def validate_reason(self, user_reason):
154
+ """
155
+ Validate user's reason against dataset reasons
156
+
157
+ :param user_reason: Reason provided by user
158
+ :return: Tuple of (is_valid, matching_reasons)
159
+ """
160
+ # Convert user reason to lowercase for matching
161
+ user_reason_lower = user_reason.lower()
162
+
163
+ # Check against predefined reasons and current sample's reasons
164
+ matching_reasons = []
165
+
166
+ # Check predefined reasons
167
+ for reason in self.predefined_reasons:
168
+ if reason.lower() in user_reason_lower:
169
+ matching_reasons.append(reason)
170
+
171
+ # Check original sample's reasons
172
+ for orig_reason in self.current_reasons:
173
+ if orig_reason.lower() in user_reason_lower:
174
+ matching_reasons.append(orig_reason)
175
+
176
+ return len(matching_reasons) > 0, matching_reasons
177
+
178
+ def save_annotation(self, guess_index, reason, reason_validity):
179
+ """
180
+ Save annotation to a text file
181
+
182
+ :param guess_index: Index of the guessed boundary
183
+ :param reason: Reason for the guess
184
+ :param reason_validity: Validity of the reason
185
+ """
186
+ # Ensure logs directory exists
187
  os.makedirs('logs', exist_ok=True)
188
+
189
+ # Generate unique filename with timestamp
190
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
191
  filename = f'logs/annotation_{timestamp}.txt'
192
+
193
+ # Prepare annotation details
194
  annotation_details = [
195
  f"Timestamp: {timestamp}",
196
  f"Model: {self.current_sample['model']}",
197
  f"Dataset: {self.current_sample['dataset']}",
198
  f"Guess Index: {guess_index + 1}",
199
  f"True Boundary Index: {self.true_boundary_index + 1}",
200
+ f"Original Dataset Reasons: {self.current_reasons}",
201
+ f"User Reason: {reason}",
202
+ f"Reason Validity: {reason_validity[0]}",
203
+ f"Matching Reasons: {reason_validity[1]}",
204
+ "\nFull Text:\n" + "\n".join(f"{i+1}. {sent}" for i, sent in enumerate(self.current_sentences))
205
  ]
206
+
207
+ # Write to file
208
  with open(filename, 'w') as f:
209
  f.write("\n".join(annotation_details))
210
 
211
  def main():
212
  local_css()
213
+
214
+ # Fancy title with animation
215
  st.markdown("""
216
+ <h1 style='text-align: center; color: #2c3e50;
217
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
218
+ animation: fadeIn 2s;'>
219
  🕵️ Real or Fake Text Detective 🕵️‍♀️
220
  </h1>
221
  """, unsafe_allow_html=True)
222
 
223
+ # Game introduction
224
+ st.markdown("""
225
+ <div class='game-container'>
226
+ <p style='text-align: center; font-style: italic;'>
227
+ Sharpen your AI detection skills! Read carefully and identify where human writing transforms into machine-generated text.
228
+ </p>
229
+ </div>
230
+ """, unsafe_allow_html=True)
231
+
232
+
233
+ # Initialize game session state
234
  if 'game' not in st.session_state:
235
  st.session_state.game = RoFTGame('roft.csv')
236
  st.session_state.game.load_random_sample()
237
  st.session_state.total_points = 0
238
  st.session_state.rounds_played = 0
239
+
240
+ # Game container
241
+ st.markdown("<div class='game-container'>", unsafe_allow_html=True)
242
+
243
+ # Game information in sidebar with icons
244
  st.sidebar.markdown("## 🎮 Game Stats")
245
  st.sidebar.markdown(f"### 🏆 Total Points: {st.session_state.total_points}")
246
  st.sidebar.markdown(f"### 🎲 Rounds Played: {st.session_state.rounds_played}")
247
 
248
+ # Animated difficulty indicator
249
+ difficulty_map = {
250
+ 'gpt2': '🟢 Easy',
251
+ 'gpt2-xl': '🟠 Medium',
252
+ 'ctrl': '🔴 Hard'
253
+ }
254
+ current_model = st.session_state.game.current_sample['model']
255
+ difficulty = difficulty_map.get(current_model, '⚪ Unknown')
256
+ st.sidebar.markdown(f"### 🎯 Difficulty: {difficulty}")
257
+
258
+ # Display sentences with enhanced styling and radio buttons
259
  st.subheader("🔍 Examine the Text Carefully")
260
+ selected_guess = st.radio(
261
+ "Where do you think the AI-generated text begins?",
262
+ options=[f"{i+1}. {sentence}" for i, sentence in enumerate(st.session_state.game.current_sentences)],
263
+ label_visibility="collapsed"
264
+ )
265
+
266
+ # Reason input with predefined options and visual enhancements
267
+ st.markdown("### 🧐 Explain Your Reasoning")
268
+ reason_options = st.session_state.game.predefined_reasons
269
+ selected_predefined_reasons = st.multiselect(
270
+ "Select indicators of AI generation",
271
+ options=reason_options
272
+ )
273
+
274
+ # Custom reason input
275
+ custom_reason = st.text_area("Additional detective notes (optional)")
276
+
277
+ # Combine predefined and custom reasons
278
+ full_reason = " ".join(selected_predefined_reasons)
279
+ if custom_reason:
280
+ full_reason += f" {custom_reason}"
281
 
282
+
283
+
284
+ # Guess submission
285
  if st.button("Submit Guess"):
286
+ if not full_reason.strip():
287
+ st.warning("Please provide a reason for your guess.")
288
  else:
289
+ # Convert guess to index (subtract 1 for 0-based indexing)
290
+ guess_index = int(selected_guess.split('.')[0]) - 1
291
+
292
+ # Check guess and update points
293
+ points_earned = st.session_state.game.check_guess(guess_index)
294
  st.session_state.total_points += points_earned
295
  st.session_state.rounds_played += 1
296
+
297
+ # If guess is correct, validate reason
298
+ if guess_index == st.session_state.game.true_boundary_index:
299
+ reason_validity = st.session_state.game.validate_reason(full_reason)
300
+ st.subheader("Results")
301
+ st.write(f"Your Guess: Sentence {selected_guess}")
302
+ st.write(f"Actual Boundary: Sentence {st.session_state.game.true_boundary_index + 1}")
303
+ st.write(f"Points Earned: {points_earned}")
304
 
305
+ # Display reason validation
306
+ st.write("Reason Validation:")
307
+ st.write(f"Valid Reason: {reason_validity[0]}")
308
+ if reason_validity[1]:
309
+ st.write("Matching Reasons:", ", ".join(reason_validity[1]))
310
 
311
+ # Save annotation
312
+ st.session_state.game.save_annotation(guess_index, full_reason, reason_validity)
 
 
 
313
  else:
314
+ st.subheader("Results")
315
+ st.write(f"Your Guess: Sentence {selected_guess}")
316
+ st.write(f"Actual Boundary: Sentence {st.session_state.game.true_boundary_index + 1}")
317
+ st.write(f"Points Earned: {points_earned}")
318
+
319
+ # Option to continue
320
  if st.button("Next Round"):
321
  st.session_state.game.load_random_sample()
322
  st.experimental_rerun()
323
+
324
  st.markdown("</div>", unsafe_allow_html=True)
325
 
326
+ # Optional: Show metadata for current sample
327
+ if st.checkbox("Show Sample Metadata"):
328
+ st.write("Current Sample Details:")
329
+ sample = st.session_state.game.current_sample
330
+ st.write(f"Model: {sample['model']}")
331
+ st.write(f"Dataset: {sample['dataset']}")
332
+ st.write(f"Sampling Strategy (p): {sample['dec_strat_value']}")
333
+ st.write(f"Original Reasons: {st.session_state.game.current_reasons}")
334
+
335
  if __name__ == "__main__":
336
  main()