Я построил двоичный классификатор 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.
Есть что-то, чего я не вижу?
Просто выяснили проблему.
Это было в скрипте, который я выполнял через 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);
getInputStream
но неgetOutputStream
?