Использование маринованного класса Python во время выполнения Java

1

Я построил двоичный классификатор SVM из некоторых строк, используя scikit-learn:

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. Я распечатал всю команду (cmd) из java и выполнил ее непосредственно в терминале, и он произвел правильный вывод. Но выход, созданный java Runtime, всегда равен 0.

Есть что-то, чего я не вижу?

  • 0
    Вы уверены, что должны использовать getInputStream но не getOutputStream ?
  • 0
    Да. В документации говорится, что getInputStream «возвращает входной поток, подключенный к обычному выходу подпроцесса. Поток получает данные, передаваемые из стандартного вывода процесса, представленного этим объектом Process».
Показать ещё 2 комментария
Теги:
scikit-learn
pickle

1 ответ

0
Лучший ответ

Просто выяснили проблему.

Это было в скрипте, который я выполнял через 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);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню