簡單使用sk-learn訓練費用會科分類的記錄
因為基本工作是涉及公司制費方面的資料,手頭上有歷年的資料,正好之前有同事提過,想根據請購描述內容進行會科分類,就順手記錄一下。
表格大概格式是: 部門名稱,部門代碼,費用屬性,傳票內容(就是請購人員註明的項目等信息), 金額,會科1,會科2,。。。,這次用到的應該只有2個欄位,
即:傳票內容,會科2; 那用第一個作為特徵,第二個就是標籤啦, 也就是要預測的結果。
一:歷史費用自理的整合
從2020年到2024年的資料,每年有12個檔案;
要用的庫:
import pandas as pd import numpy as np import xlrd import os import re import jieba
整合資料的代碼:
def standardize_column_names(df, column_mapping): """将DataFrame的列名标准化""" return df.rename(columns=column_mapping, errors='ignore') def read_excel(folder_path, keyword, column_mapping): all_data = pd.DataFrame() for root, dirs, files in tqdm(os.walk(folder_path)): for filename in tqdm(files): if filename.endswith('.xlsx') or filename.endswith('.xls'): file_path = os.path.join(root, filename) print('读取文件' + file_path) try: xls = pd.ExcelFile(file_path) for sheet_name in xls.sheet_names: if keyword in sheet_name: data = pd.read_excel(file_path, sheet_name=sheet_name) data = standardize_column_names(data, column_mapping) #只保留映射后的列 data = data[list(column_mapping.values())] all_data = pd.concat([all_data, data], ignore_index=True) except Exception as e: print(f"Error reading {filename}: {e}") return all_data folder_path = './cost_20-24' keyword = 'GL46' column_mapping = { '會科2': '會科2', '会科2': '會科2', '傳票摘要內容': '傳票摘要內容', '傳票摘要與內容': '傳票摘要內容', } combined_data = read_excel(folder_path, keyword, column_mapping) combined_data.to_csv("20_24combined_data.csv", index=False) combined_data.info()
因為裡面存在繁體和簡體的問題,所以有定義一個通用的功能, 無論是簡體還是繁體都抓進來. 後面會再去掉多餘的2列的.
這裡數據大約有55萬筆;
二: 原始數據集整理為待訓練的數據集
比如,去除多餘列,將不要的會科分類去除(如人工費,包裝費),去除人工費是因為傳票內容日期太多,不利於訓練,而除去包裝費是因為原始數據集中包裝費幾乎沒有數據。
代碼如下;
datas = combined_data.iloc[:,[0,2]] datas = datas.dropna() data = datas[~datas["會科2"].str.contains("人工", na=False)] data = data[~data["會科2"].str.contains("包裝費", na=False)] data.info()
到這裡後,還有大約16萬筆數據;
三:數據集預處理
對數據進行清洗文本、分词和去除停用词等~
# 读取停用词文档 def load_stopwords(stopwords_path): with open(stopwords_path, 'r', encoding='utf-8') as f: stopwords = set(line.strip() for line in f) return stopwords # 文本预处理函数,包括清洗文本、分词和去除停用词 def preprocess_text(text, stopwords): # 清洗文本,保留中文字符 text = re.sub(r'[^\u4e00-\u9fa5]+', '', text) # 对中文文本进行分词 words = jieba.cut(text) # 去除停用词 filtered_words = [word for word in words if word not in stopwords] return ' '.join(filtered_words) # 加载停用词 stopwords = load_stopwords('stopwords_hit.txt') # 替换为您的停用词文档路径 # 假设 data_drop 是已经存在的 DataFrame # 将日期时间对象转换为字符串格式 data_drop['會科2'] = data_drop['會科2'].apply(lambda x: x.strftime('%Y-%m-%d') if isinstance(x, datetime) else x) # 预处理所有文本 data_drop['傳票摘要內容'] = data_drop['傳票摘要內容'].apply(lambda x: preprocess_text(x, stopwords)) # 提取文本和标签 texts = data_drop['傳票摘要內容'].tolist() labels = data_drop['會科2'].tolist() # 保存处理后的数据到 Excel 文件 data_drop.to_excel("20_24data_drop01.xlsx", index=False)
四:進行訓練和保存模型
需要的庫:
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB from sklearn.metrics import accuracy_score, classification_report, confusion_matrix import joblib from datetime import datetime
下面是進行:初始化TF-IDF向量,數據集拆分,訓練並評估
# 初始化TF-IDF向量化器 vectorizer = TfidfVectorizer() # 提取特征 features = vectorizer.fit_transform(texts) # 将数据集分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.3, random_state=42) # 选择朴素贝叶斯分类器作为基础模型 model = MultinomialNB() # 使用训练集数据训练模型 model.fit(X_train, y_train) # 模型预测和评估 y_pred = model.predict(X_test) print("Accuracy:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred))
評分結果:
Accuracy: 0.914814970386861 precision recall f1-score support 修繕費(設備) 0.91 0.91 0.91 3673 固定成本-Others 0.88 0.97 0.92 22860 折舊-廠房 0.99 0.97 0.98 2107 折舊-設備 0.89 0.60 0.72 3214 模具費 0.99 0.95 0.97 1928 水電瓦斯費 1.00 1.00 1.00 10363 消耗品 0.81 0.64 0.71 2582 雜項購置 0.85 0.34 0.49 887 accuracy 0.91 47614 macro avg 0.91 0.80 0.84 47614 weighted avg 0.91 0.91 0.91 47614 [[ 3335 308 0 0 4 0 22 4] [ 245 22097 21 246 15 3 216 17] [ 0 67 2040 0 0 0 0 0] [ 0 1275 0 1939 0 0 0 0] [ 7 82 0 0 1834 0 5 0] [ 0 0 0 0 0 10363 0 0] [ 34 866 0 0 3 0 1644 35] [ 47 394 0 0 3 0 137 306]]
# 保存訓練後的模型 joblib.dump(model, 'trained_model.pkl') joblib.dump(vectorizer, 'vectorizer.pkl')
使用新的費用表的傳票內容進行測試:
df = pd.read_excel('Test202502.xlsx') texts01 = df['傳票摘要內容'].tolist() def preprocess_text(text): return ' '.join(jieba.cut(text)) processed_texts = [preprocess_text(text) for text in texts01] features = vectorizer.transform(processed_texts) # 使用模型进行预测 predicted_labels = model.predict(features) # 将预测结果添加到原始DataFrame中作为新列 df['predicted_label'] = predicted_labels # 将包含预测结果的DataFrame保存到新的Excel文件中 output_file = 'predicted_results.xlsx' df.to_excel(output_file, index=False) # index=False表示不保存行索引 print(f"预测结果已保存到文件: {output_file}")