首页 > 解决方案 > 通过 java 运行时使用腌制的 python 类

问题描述

我使用 scikit-learn 从一些字符串构建了一个二进制 SVM 分类器:

count_vect = TfidfVectorizer(sublinear_tf=True, min_df=10, norm='l2', encoding='latin-1', ngram_range=(1, 4))
X_counts = count_vect.fit_transform(X)
tfidf_transformer = TfidfTransformer()
X_tfidf = tfidf_transformer.fit_transform(X_counts)
clf = SGDClassifier(loss='hinge', penalty='l2', tol=1e-3)
clf.fit(X_tfidf, Y)

然后我围绕经过训练的分类器创建了一个包装类并将其腌制:

class Classifier:
    def __init__(self, clf, vect):
        self.classifier = clf
        self.vectorizer = vect

    def classify(self, s):
        return self.classifier.predict(self.vectorizer.transform([s]))[0]

with open('my_classifier.pkl', 'wb') as fout:
    pickle.dump(Classifier(clf, count_vect), fout)

在单独的脚本中,我可以加载腌制分类器并正确使用它:

with open('my_classifier.pkl', 'rb') as fin:
    clf = pickle.load(fin)

result = clf.classify(sys.argv[1])
print(result)

但是,当我尝试通过 java Runtime 执行脚本时,它显示不正确的输出。

public boolean classify(String s) throws IOException {
        String cmd = "python3 pkl_classifier.py \"" + s + "\"";
        Process p = Runtime.getRuntime().exec(cmd);
        BufferedReader stdIn = new BufferedReader(new InputStreamReader(p.getInputStream()));

        String out = stdIn.readLine();
        if (out != null) {
            switch (Integer.parseInt(out)) {
                case 0: return false;
                case 1: return true;
                default: throw new RuntimeException("Error with classifier script:" + out);
            }
        }

        return false;
    }

分类器的输出可以是 0 或 1。但是这个 java 代码总是产生一个 0。我已经从 java 打印出整个命令 ( cmd) 并直接在终端中执行它,它产生了正确的输出。但是java Runtime产生的输出总是0。

有什么我想念的吗?

标签: javapythonscikit-learnpickle

解决方案


才发现问题。

它在我通过java执行的脚本中。

假设我要分类的字符串是"lorem ipsum dolor sit amet". 当我形成命令在我的 java 类中将其分类为

String cmd = "python3 pkl_classifier.py \"" + s + "\"";

它看起来像

python3 pkl_classifier.py "lorem ipsum dolor sit amet"

这正是我想要执行的命令。当我打印出变量cmd时,它看起来还不错。但是当我在 python 脚本中使用打印命令时

print(sys.argv)

它看起来像

['pkl_classifier.py', '"lorem', 'ipsum', 'dolor', 'sit', 'amet"']

因此,显然它是通过忽略引号的空格来拆分命令。

然后我改变了脚本

result = clf.classify(sys.argv[1])

result = clf.classify(' '.join(sys.argv[1:]))

它工作得很好!

更好的解决方案:

与其调整 python 脚本,不如使用其他将参数作为字符串数组的类exec()方法。Process它将在内部处理参数中的空格。

String[] cmd = { "python3", "pkl_classifier.py", s };
p = Runtime.getRuntime().exec(cmd);

推荐阅读