{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":10737,"databundleVersionId":290346,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":519432,"sourceType":"modelInstanceVersion","isSourceIdPinned":false,"modelInstanceId":408997,"modelId":426851}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Part 1: Training Script\nimport torch\nimport numpy as np\nimport pandas as pd\nfrom datasets import load_dataset, ClassLabel\nfrom sklearn.metrics import f1_score\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom transformers import (\n    BertTokenizerFast,\n    BertForSequenceClassification,\n    Trainer,\n    TrainingArguments,\n    DataCollatorWithPadding\n)\nimport os\n\nprint(\"=\"*60)\nprint(\"Part 1: Training and Saving the Model\")\nprint(\"=\"*60)\n\n# --- 1. 环境与路径设置 ---\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n# 预训练模型的路径 (从Kaggle数据集加载)\nPRETRAINED_MODEL_PATH = 'bert-base-uncased'\n# 训练好的模型的保存路径\nFINAL_MODEL_OUTPUT_PATH = './final_model'\n\nos.environ['TOKENIZERS_PARALLELISM'] = 'false'\nprint(f\"Using device: {device}\")\nprint(f\"Loading pretrained model from: {PRETRAINED_MODEL_PATH}\")\nprint(f\"Final model will be saved to: {FINAL_MODEL_OUTPUT_PATH}\")\n\n# --- 2. 加载和预处理数据 ---\nprint(\"\\nLoading and preparing data...\")\ndataset = load_dataset(\n    'csv',\n    data_files={'train': '/kaggle/input/quora-insincere-questions-classification/train.csv'},\n    delimiter=','\n)\n\nclass_label_feature = ClassLabel(num_classes=2, names=['sincere', 'insincere'])\ndataset['train'] = dataset['train'].cast_column('target', class_label_feature)\n\nsplit = dataset['train'].train_test_split(test_size=0.1, seed=42, stratify_by_column='target')\ntrain_ds = split['train']\nvalid_ds = split['test']\nprint(f\"Training set: {len(train_ds)} samples | Validation set: {len(valid_ds)} samples\")\n\n# --- 3. 初始化分词器和模型 ---\ntokenizer = BertTokenizerFast.from_pretrained(PRETRAINED_MODEL_PATH)\nmodel = BertForSequenceClassification.from_pretrained(PRETRAINED_MODEL_PATH, num_labels=2)\nmodel.to(device)\n\n# --- 4. 数据编码 ---\nmax_length = 64\ndef tokenize_fn(examples):\n    return tokenizer(\n        examples['question_text'],\n        truncation=True,\n        max_length=max_length\n    )\n\nprint(\"\\nEncoding datasets...\")\ntrain_ds = train_ds.map(tokenize_fn, batched=True, remove_columns=['question_text'])\nvalid_ds = valid_ds.map(tokenize_fn, batched=True, remove_columns=['question_text'])\ntrain_ds = train_ds.rename_column(\"target\", \"labels\")\nvalid_ds = valid_ds.rename_column(\"target\", \"labels\")\ndata_collator = DataCollatorWithPadding(tokenizer=tokenizer)\nprint(\"Encoding complete.\")\n\n# --- 5. 定义评估指标和带权重的损失函数 ---\ndef compute_metrics(eval_pred):\n    logits, labels = eval_pred\n    preds = np.argmax(logits, axis=1)\n    return {'f1': f1_score(labels, preds)}\n\nclass_weights = compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(train_ds['labels']),\n    y=train_ds['labels']\n)\nclass_weights = torch.tensor(class_weights, dtype=torch.float).to(device)\n\nclass WeightedLossTrainer(Trainer):\n    def compute_loss(self, model, inputs, return_outputs=False, **kwargs):\n        labels = inputs.pop(\"labels\")\n        outputs = model(**inputs)\n        logits = outputs.get(\"logits\")\n        loss_fct = torch.nn.CrossEntropyLoss(weight=class_weights)\n        loss = loss_fct(logits.view(-1, self.model.config.num_labels), labels.view(-1))\n        return (loss, outputs) if return_outputs else loss\n\n# --- 6. 配置训练参数 ---\nprint(\"\\nConfiguring training arguments...\")\ntraining_args = TrainingArguments(\n    output_dir='./results',  # 检查点保存目录\n    num_train_epochs=1,\n    per_device_train_batch_size=32,\n    per_device_eval_batch_size=64,\n    gradient_accumulation_steps=2,\n    learning_rate=2e-5,\n    weight_decay=0.01,\n    lr_scheduler_type='cosine',\n    warmup_ratio=0.1,\n    logging_steps=100,\n    eval_strategy=\"steps\",\n    eval_steps=500,\n    save_strategy=\"steps\",\n    save_steps=500,\n    save_total_limit=1,\n    fp16=True,\n    load_best_model_at_end=True,\n    metric_for_best_model='f1',\n    greater_is_better=True,\n    report_to=\"none\",\n)\n\n# --- 7. 创建并开始训练 ---\ntrainer = WeightedLossTrainer(\n    model=model,\n    args=training_args,\n    train_dataset=train_ds,\n    eval_dataset=valid_ds,\n    compute_metrics=compute_metrics,\n    tokenizer=tokenizer,\n    data_collator=data_collator,\n)\n\nprint(\"\\n\" + \"=\"*60)\nprint(\"Starting training...\")\nprint(\"=\"*60)\ntrainer.train()\nprint(\"\\nTraining complete!\")\n\n# --- 8. 评估并保存最终模型 ---\nprint(\"\\nFinal evaluation on validation set...\")\neval_results = trainer.evaluate()\nprint(f\"Validation set F1 Score: {eval_results.get('eval_f1', 'N/A'):.4f}\")\n\nprint(f\"\\nSaving the best model to '{FINAL_MODEL_OUTPUT_PATH}'...\")\ntrainer.save_model(FINAL_MODEL_OUTPUT_PATH)\nprint(\"Model saved successfully.\")\nprint(\"\\nPart 1 finished.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-08-13T07:10:52.124973Z","iopub.execute_input":"2025-08-13T07:10:52.125745Z","iopub.status.idle":"2025-08-13T07:16:01.180954Z","shell.execute_reply.started":"2025-08-13T07:10:52.125721Z","shell.execute_reply":"2025-08-13T07:16:01.179883Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}