Download Back-end/data_processing/process_qa.py from same899/Chatbot: direct link, hf CLI and curl.
- Browser
- Download file 7.91 kB
-
https://huggingface.co/same899/Chatbot/resolve/d67410f4faaf732d8db334fedf7f2d63def66f77/Back-end/data_processing/process_qa.py
- Command line
-
hf download hf://same899/Chatbot@d67410f4faaf732d8db334fedf7f2d63def66f77/Back-end/data_processing/process_qa.py
-
curl -L -o process_qa.py https://huggingface.co/same899/Chatbot/resolve/d67410f4faaf732d8db334fedf7f2d63def66f77/Back-end/data_processing/process_qa.py
7.91 kB
| import json | |
| import os | |
| import re | |
| import pandas as pd | |
| import nltk | |
| from nltk.corpus import stopwords | |
| from nltk.stem import WordNetLemmatizer | |
| from sklearn.feature_extraction.text import TfidfVectorizer | |
| from typing import List, Dict, Any | |
| from tqdm import tqdm | |
| import hashlib | |
| # 下载必要的NLTK资源 | |
| required_packages = ['punkt', 'stopwords', 'wordnet'] | |
| for package in required_packages: | |
| try: | |
| nltk.data.find(f'tokenizers/{package}' if package == 'punkt' else f'corpora/{package}') | |
| print(f"Package {package} is already downloaded.") | |
| except LookupError: | |
| print(f"Downloading NLTK {package}...") | |
| nltk.download(package) | |
| class QAProcessor: | |
| def __init__(self, data_dir: str, output_path: str): | |
| self.data_dir = data_dir | |
| self.output_path = output_path | |
| self.lemmatizer = WordNetLemmatizer() | |
| self.stop_words = set(stopwords.words('english')) | |
| self.medical_stopwords = {'disease', 'patient', 'treatment', 'condition', 'symptom', 'doctor', 'health', 'may', 'also', 'one', 'use'} | |
| self.stop_words.update(self.medical_stopwords) | |
| def clean_text(self, text: str) -> str: | |
| """清理文本""" | |
| if isinstance(text, str): | |
| # 移除HTML标签 | |
| text = re.sub(r'<.*?>', ' ', text) | |
| # 移除特殊字符但保留字母和数字 | |
| text = re.sub(r'[^\w\s]', ' ', text) | |
| # 移除多余的空格 | |
| text = re.sub(r'\s+', ' ', text).strip().lower() | |
| return text | |
| return "" | |
| def simple_tokenize(self, text: str) -> List[str]: | |
| """简单的分词函数""" | |
| text = text.lower().strip() | |
| tokens = re.findall(r'\b\w+\b', text) | |
| return tokens | |
| def extract_keywords(self, text: str, top_n: int = 10) -> List[str]: | |
| """使用TF-IDF提取关键词""" | |
| if not isinstance(text, str) or not text.strip(): | |
| return [] | |
| tokens = self.simple_tokenize(text) | |
| filtered_tokens = [self.lemmatizer.lemmatize(token) | |
| for token in tokens | |
| if token.isalpha() and token not in self.stop_words and len(token) > 2] | |
| if filtered_tokens: | |
| vectorizer = TfidfVectorizer(max_features=top_n) | |
| try: | |
| tfidf_matrix = vectorizer.fit_transform([' '.join(filtered_tokens)]) | |
| feature_names = vectorizer.get_feature_names_out() | |
| scores = zip(feature_names, tfidf_matrix.toarray()[0]) | |
| sorted_scores = sorted(scores, key=lambda x: x[1], reverse=True) | |
| return [word for word, score in sorted_scores[:top_n] if score > 0] | |
| except Exception as e: | |
| print(f"TF-IDF提取失败: {e}") | |
| return filtered_tokens[:top_n] | |
| return [] | |
| def process_data(self) -> tuple[List[Dict[str, Any]], Dict[str, List[str]]]: | |
| """处理所有数据源""" | |
| qa_database = [] | |
| keyword_index = {} # 倒排索引:keyword → [qa_id1, qa_id2, ...] | |
| # 处理Healthline文章 | |
| healthline_path = os.path.join(self.data_dir,'Healthline', 'healthline_articles_text.csv') | |
| if os.path.exists(healthline_path): | |
| print("处理Healthline数据...") | |
| healthline_df = pd.read_csv(healthline_path) | |
| for idx, row in tqdm(healthline_df.iterrows(), total=len(healthline_df)): | |
| title = row.get('title', '') | |
| content = row.get('content', '') | |
| if not isinstance(title, str) or not isinstance(content, str): | |
| continue | |
| clean_title = self.clean_text(title) | |
| clean_content = self.clean_text(content) | |
| qa_pair = { | |
| 'id': f"healthline_{idx}", | |
| 'source': 'healthline', | |
| 'question': clean_title, | |
| 'answer': clean_content, | |
| 'keywords': self.extract_keywords(clean_title + " " + clean_content) | |
| } | |
| qa_database.append(qa_pair) | |
| # 更新倒排索引 | |
| for keyword in qa_pair['keywords']: | |
| if keyword not in keyword_index: | |
| keyword_index[keyword] = [] | |
| keyword_index[keyword].append(qa_pair['id']) | |
| # 处理MedQA数据 | |
| medqa_dir = os.path.join(self.data_dir, 'MedQA') | |
| if os.path.exists(medqa_dir): | |
| print("处理MedQA数据...") | |
| for file_name in os.listdir(medqa_dir): | |
| if file_name.endswith('.csv'): | |
| dataset_name = file_name.split('.')[0] | |
| df = pd.read_csv(os.path.join(medqa_dir, file_name)) | |
| question_col = next((col for col in ['Question', 'question'] if col in df.columns), None) | |
| answer_col = next((col for col in ['Answer', 'answer'] if col in df.columns), None) | |
| if not question_col or not answer_col: | |
| print(f"跳过 {dataset_name} - 缺少问题/答案列") | |
| continue | |
| for idx, row in tqdm(df.iterrows(), total=len(df)): | |
| question = row.get(question_col, '') | |
| answer = row.get(answer_col, '') | |
| if not isinstance(question, str) or not isinstance(answer, str): | |
| continue | |
| clean_question = self.clean_text(question) | |
| clean_answer = self.clean_text(answer) | |
| qa_pair = { | |
| 'id': f"{dataset_name}_{idx}", | |
| 'source': dataset_name, | |
| 'question': clean_question, | |
| 'answer': clean_answer, | |
| 'keywords': self.extract_keywords(clean_question + " " + clean_answer) | |
| } | |
| qa_database.append(qa_pair) | |
| for keyword in qa_pair['keywords']: | |
| if keyword not in keyword_index: | |
| keyword_index[keyword] = [] | |
| keyword_index[keyword].append(qa_pair['id']) | |
| return qa_database, keyword_index | |
| def save_results(self, qa_database: List[Dict[str, Any]], keyword_index: Dict[str, List[str]]): | |
| """保存处理后的数据""" | |
| qa_output = os.path.join(self.output_path, 'cleaned_qa','qa_database.json') | |
| keyword_output = os.path.join(self.output_path, 'keywords','keyword_index.json') | |
| os.makedirs(os.path.dirname(qa_output), exist_ok=True) | |
| os.makedirs(os.path.dirname(keyword_output), exist_ok=True) | |
| with open(qa_output, 'w', encoding='utf-8') as f: | |
| json.dump(qa_database, f, ensure_ascii=False, indent=2) | |
| with open(keyword_output, 'w', encoding='utf-8') as f: | |
| json.dump(keyword_index, f, ensure_ascii=False, indent=2) | |
| print(f"处理完成的QA对数量: {len(qa_database)}") | |
| print(f"关键词索引中的关键词数量: {len(keyword_index)}") | |
| print(f"数据已保存到: {self.output_path}") | |
| if __name__ == "__main__": | |
| # 设置路径 | |
| data_dir = './Data/raw' | |
| output_path = './Data/Processed/' | |
| # 创建处理器实例并处理数据 | |
| processor = QAProcessor(data_dir, output_path) | |
| qa_database, keyword_index = processor.process_data() | |
| processor.save_results(qa_database, keyword_index) |