使用机器学习进行文本标记

Text labeling with machine learning

我想根据一组预定义的 类 标记一堆银行交易(下面的示例,它是 clojure 中的地图)。我尝试了一种朴素的贝叶斯方法,但有时它完全给我错误的标签。

根据我的研究,我应该使用监督 ML 算法,类似于针对多类分类调整的线性 SVM。问题是我真的对机器学习一无所知。第二个问题是大多数 clojure 库都过时了。

{:label "5339134-17-CPR-FARMODISSEIA LD", :value -13271 :class :health}
{:label "PAG.SERV. 10297 779747511", :value -2889 :class :utilities}
{:label "5339134-14-CPR-GREEN PEPER", :value -1785 :class :restaurants}
{:label "5339134-03-LEV-Av Alm Kings", :value -4000 :class :atm}
{:label "5339134-02-LEV-Big Field, 1", :value -7000 :class :atm}
{:label "IMPOSTO DE SELO", :value -17 :class :banking}

所以大多数类似的交易都有90%的相似文本(参见示例::atm),我相信这应该是一个简单的问题。

我的问题:

clj 或 java 中的任何示例将不胜感激。

既然你在问题中说过

most of the similar transactions have like 90% similar text

我认为首先找出哪些交易标签彼此相似并将它们组合在一起是有意义的。然后你有有限数量的组,每个标签所属的组可以用作名义属性来代替文本本身。如果同一 class 中的交易具有相似的标签文本,那么希望这应该允许 classification 算法轻松绘制标签与 class.

之间的相关性

我尝试使用这些依赖项实现解决方案:

[[org.clojure/clojure "1.8.0"]
 [clj-fuzzy "0.4.0"]
 [cc.artifice/clj-ml "0.8.5"]
 [rm-hull/clustering "0.1.3"]]

对标签进行聚类后,朴素贝叶斯方法似乎对我很有效:

(require '[clj-fuzzy.metrics :as fm]
         '[clj-ml.classifiers :as classify]
         '[clj-ml.data :as data]
         '[clustering.core.qt :as qt])

(def data
  [{:label "5339134-17-CPR-FARMODISSEIA LD", :value -13271 :class :health}
   {:label "PAG.SERV. 10297 779747511", :value -2889 :class :utilities}
   {:label "5339134-14-CPR-GREEN PEPER", :value -1785 :class :restaurants}
   {:label "5339134-03-LEV-Av Alm Kings", :value -4000 :class :atm}
   {:label "5339134-02-LEV-Big Field, 1", :value -7000 :class :atm}
   {:label "IMPOSTO DE SELO", :value -17 :class :banking}])

(def clusters
  (into {}
        (for [cluster (qt/cluster fm/levenshtein (map :label data) 13 1)
              s cluster]
          [s (keyword (str "cluster" (hash cluster)))])))

(def dataset
  (-> (data/make-dataset "my-data"
                         [:value
                          {:label (seq (set (vals clusters)))}
                          {:class [:health :utilities :restaurants :atm :banking]}]
                         (map (juxt :value (comp clusters :label) :class) data))
      (data/dataset-set-class :class)))

(def data-map
  (let [m (into {} (map (juxt data/instance-to-map identity)
                        (data/dataset-seq dataset)))]
    (into {} (for [x data]
               [x (-> x (update :label clusters) (update :value double) m)]))))

(def classifier
  (-> (classify/make-classifier :bayes :naive)
      (classify/classifier-train dataset)))

(defn foo []
  (for [x data]
     (->> x
          data-map
          data/instance-set-class-missing
          (classify/classifier-classify classifier)
          (assoc x :predicted))))

(run! prn (foo))
;; {:label "5339134-17-CPR-FARMODISSEIA LD", :value -13271, :class :health, :predicted :health}
;; {:label "PAG.SERV. 10297 779747511", :value -2889, :class :utilities, :predicted :utilities}
;; {:label "5339134-14-CPR-GREEN PEPER", :value -1785, :class :restaurants, :predicted :restaurants}
;; {:label "5339134-03-LEV-Av Alm Kings", :value -4000, :class :atm, :predicted :atm}
;; {:label "5339134-02-LEV-Big Field, 1", :value -7000, :class :atm, :predicted :atm}
;; {:label "IMPOSTO DE SELO", :value -17, :class :banking, :predicted :banking}

虽然我对 ML 很陌生,所以如果有什么我忽略的地方请告诉我。

此外,在我的实现中,我使用 QT 聚类对输入数据集中的标签进行一次性分区,但如果目标是随着时间的推移继续合并新数据,则可能需要使用流聚类算法代替。看起来这可能用 k-means 可行,但这需要实现 "Levenshtein averaging" 函数。另外,我不确定我使用的聚类库是否支持基于其初始结果的迭代,因此可能需要进一步实施。