#java #machine-learning
#java #машинное обучение
Вопрос:
У меня есть большой файл с 1,8 миллионами строк данных, которые мне нужно иметь возможность читать для программы машинного обучения, которую я пишу. Данные в настоящее время находятся в файле CSV, но, очевидно, я могу поместить их в базу данных или другую структуру по мере необходимости — их не нужно будет регулярно обновлять.
Код, который я использую в данный момент, приведен ниже. Сначала я импортирую данные в список массивов, а затем передаю их в табличную модель. Это очень медленно, в настоящее время на выполнение только первых 10 000 строк уходит шесть минут, что неприемлемо, поскольку мне нужно иметь возможность довольно часто тестировать различные алгоритмы на данных.
Моей программе потребуется получить доступ к каждой строке данных только один раз, поэтому нет необходимости хранить весь набор данных в оперативной памяти. Мне лучше читать из базы данных, или есть лучший способ читать CSV-файл построчно, но делать это намного быстрее?
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class CSVpaser {
public static TableModel parse(File f) throws FileNotFoundException {
ArrayList<String> headers = new ArrayList<String>();
ArrayList<String> oneDdata = new ArrayList<String>();
//Get the headers of the table.
Scanner lineScan = new Scanner(f);
Scanner s = new Scanner(lineScan.nextLine());
s.useDelimiter(",");
while (s.hasNext()) {
headers.add(s.next());
}
//Now go through each line of the table and add each cell to the array list
while (lineScan.hasNextLine()) {
s = new Scanner(lineScan.nextLine());
s.useDelimiter(", *");
while (s.hasNext()) {
oneDdata.add(s.next());
}
}
String[][] data = new String[oneDdata.size()/headers.size()][headers.size()];
int numberRows = oneDdata.size()/headers.size();
// Move the data into a vanilla array so it can be put in a table.
for (int x = 0; x < numberRows; x ) {
for (int y = 0; y < headers.size(); y ) {
data[x][y] = oneDdata.remove(0);
}
}
// Create a table and return it
return new DefaultTableModel(data, headers.toArray());
}
Обновить:
Основываясь на отзывах, которые я получил в ответах, я переписал код, теперь он выполняется за 3 секунды, а не за 6 минут (для 10 000 строк), что означает всего десять минут для всего файла… но любые дальнейшие предложения о том, как ускорить это, были бы оценены:
//load data file
File f = new File("data/primary_training_short.csv");
Scanner lineScan = new Scanner(f);
Scanner s = new Scanner(lineScan.nextLine());
s.useDelimiter(",");
//now go through each line of the results
while (lineScan.hasNextLine()) {
s = new Scanner(lineScan.nextLine());
s.useDelimiter(", *");
String[] data = new String[NUM_COLUMNS];
//get the data out of the CSV file so I can access it
int x = 0;
while (s.hasNext()) {
data[x] = (s.next());
x ;
}
//insert code here which is excecuted each line
}
Ответ №1:
data[x][y] = oneDdata.remove(0);
Это было бы очень неэффективно. Каждый раз, когда вы удаляете первую запись из ArrayList, все остальные записи нужно будет сдвигать вниз.
Как минимум, вы хотели бы создать пользовательскую табличную модель, чтобы вам не приходилось копировать данные дважды.
Если вы хотите сохранить данные в базе данных, найдите в сети табличную модель ResultSet.
Если вы хотите сохранить его в формате CSV, вы можете использовать ArrayList в качестве хранилища данных для TableModel. Таким образом, ваш код сканера будет считывать данные непосредственно в ArrayList. Смотрите Модель таблицы списка для одного из таких решений. Или вы можете захотеть использовать модель Bean Table.
Конечно, реальный вопрос в том, у кого будет время просмотреть все 1,8 млн записей? Итак, вам действительно следует использовать базу данных и иметь логику запросов для фильтрации строк, возвращаемых из базы данных.
Моей программе потребуется получить доступ к каждой строке данных только один раз, поэтому нет необходимости хранить весь набор данных в оперативной памяти
Итак, почему вы отображаете его в JTable? Это подразумевает, что все данные будут находиться в памяти.
Комментарии:
1. Спасибо, я попробую переработать это, чтобы избежать функции удаления, и дам вам знать, как у меня идут дела
2. Я избавился от JTable и функций .remove, и теперь он запускается за 3 секунды вместо 6 минут. Это означает, что при использовании CSV-файла вся таблица займет 10 минут — будет ли быстрее, если я буду читать из базы данных sqllite? Мне все равно нужно было бы получить доступ к каждой строке в базе данных, чтобы запустить алгоритм
3. Насколько я знаю, доступ к базе данных будет медленнее, если вы используете ее просто для последовательного извлечения всех записей, но я уверен, что у других на форуме будет идея получше. Вам следует обновить свой код, чтобы мы могли проверить наличие других улучшений. Например, попробуйте создать ArrayList с более разумным количеством записей, чтобы ему не приходилось постоянно выделять больше места при заполнении.
4. Я подозреваю, что вы также сможете повысить производительность, улучшив маркировку вашего файла. Сканер прост в использовании, но любой общий анализатор не будет таким эффективным, как простой анализатор. Может быть, вы можете просто использовать BufferedReader для чтения файла и маркировать каждую строку с помощью StringTokenizer.
5. Я опубликовал свой обновленный код выше. В основном вы уже ответили на этот вопрос, но я потрачу некоторое время на изучение bufferedreader, чтобы посмотреть, смогу ли я работать еще быстрее…
Ответ №2:
Sqllite — это очень легкая файловая база данных, и, по моему мнению, это лучшее решение для вашей проблемы.
Ознакомьтесь с этим очень хорошим драйвером для Java. Я использую его для одного из своих проектов NLP, и он работает действительно хорошо.
Комментарии:
1. Спасибо, полезный ответ. Я собираюсь попробовать пока придерживаться CSV, чтобы мне не пришлось изучать новые классы, но если это не сработает, я обязательно попробую это…
Ответ №3:
Это то, что я понял: ваше требование — выполнить некоторый алгоритм для загруженных данных, и это тоже во время выполнения, т. Е.
- загрузите набор данных
- Выполните некоторые вычисления
- Загрузите другой набор данных
- Выполните дополнительные вычисления и так далее, пока мы не дойдем до конца CSV
Поскольку между двумя наборами данных нет корреляции, а алгоритм / вычисления, которые вы выполняете с данными, — это пользовательская логика (для которой в SQL нет встроенной функции), это означает, что вы можете сделать это на Java даже без использования какой-либо базы данных, и это должно быть быстрее всего.
Однако, если логика / вычисления, которые вы выполняете с двумя наборами данных, имеют некоторую эквивалентную функцию в SQL, и существует отдельная база данных, работающая на хорошем оборудовании (то есть больше памяти / процессора), выполнение всей этой логики с помощью процедуры / функции в SQL может выполняться лучше.
Ответ №4:
Вы можете использовать пакет opencsv, их CSVReader может выполнять итерации по большим CSV-файлам, вам также следует использовать онлайн-методы обучения, такие как NaiveBayes, LinearRegression для таких больших данных.