用机器学习标记文本

时间:2017-07-13 18:19:45

标签: java machine-learning clojure text-classification

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

根据我的研究,我应该使用有监督的ML算法,类似于针对多类分类调整的线性SVM。问题是我真的对ML一无所知。第二个问题是大多数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),我相信这应该是一个简单的问题。

我的问题:

  • 我可以使用哪些算法?
  • 我该如何准备数据?我相信我只有两个功能,tx标签和tx值。我看到的一些教程有很多向量,但我不知道是否/如何将字符串数据转换为正确的ML格式。

非常感谢clj或java中的任何样本。

1 个答案:

答案 0 :(得分:1)

既然你在问题中说过

  

大多数类似交易都有类似90%的文字

我认为首先弄清楚哪些交易标签彼此相似并将它们组合在一起是有意义的。然后,您拥有有限数量的组,每个标签所属的组可以用作名义属性来代替文本本身。如果同一类中的事务具有相似的标签文本,那么希望这应该允许分类算法轻松地绘制标签和类之间的相关性。

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

[[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平均”功能。另外,我不确定我使用的集群库是否支持迭代初始结果,因此可能需要进一步实现。