簡單使用sk-learn訓練費用會科分類的記錄

作者20251个月前 (03-12)sklearn21

因為基本工作是涉及公司制費方面的資料,手頭上有歷年的資料,正好之前有同事提過,想根據請購描述內容進行會科分類,就順手記錄一下。

表格大概格式是: 部門名稱,部門代碼,費用屬性,傳票內容(就是請購人員註明的項目等信息), 金額,會科1,會科2,。。。,這次用到的應該只有2個欄位,

即:傳票內容,會科2; 那用第一個作為特徵,第二個就是標籤啦, 也就是要預測的結果。 

一:歷史費用自理的整合

從2020年到2024年的資料,每年有12個檔案;

1.png

要用的庫:

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}")