Я покажу, как простое изменение точности ваших числовых данных может сократить использование памяти наполовину или даже больше.
Вы когда-нибудь сталкивались с проблемой памяти в MATLAB и искали ее в Интернете?
Оказывается, № 7 во многих случаях является самым простым решением и может сократить использование памяти до 90%. Этот метод имеет дополнительное преимущество: если вы сохраните свои переменные, они также будут занимать меньше места на жестком диске и загружаться гораздо быстрее.
Давайте углубимся, чтобы понять, как мы можем уменьшить использование памяти (концепции применимы и к другим средам программирования, но я сосредоточусь на MATLAB):
Важнейшие команды, обсуждаемые в этом посте:
whos
single
double
int16
format
Когда дело доходит до числовых данных, вы можете указать тип с помощью single
или double
, но люди почти никогда этого не делают! Что это значит для ваших данных? Давайте посмотрим на несколько случайных примеров:
mydata = rand(1000,1000); % Do not omit this semi-colon! whos mydata
Вы увидите таблицу, в которой указано, что ваша переменная mydata имеет значение в байтах, равное: 8 000 000. Это 8 мегабайт. Сохранится в вашей памяти.
Рядом с байтами будет написано «двойной». MATLAB по умолчанию создает очень расточительную переменную двойной точности. Попробуй это:
mydata = single( rand(1000,1000) ); % Do not omit this semi-colon! whos mydata
Что мы теряем, переходя от двойного к одинарному?
mydata_double = rand(1000,1000); mydata_single = single( mydata_double ); mean(mydata_double,'all') % No semi-colons here mean(mydata_single,'all')
Вы увидите, что результат для переменной с двойной точностью — «0,500049032047244», тогда как результат для переменной с одинарной точностью — «0,5000491». Если разница между 5 000 490 и 5 000 491 существенно повлияет на ваши данные, не преобразовывайте в одно число.
Можем ли мы сделать еще лучше с целыми числами?
Да. С оговорками. Если вас устраивают 50%, вы можете прекратить чтение здесь. Однако, если вы готовы пройти лишнюю милю, продолжайте. Вы должны делать это только в том случае, если вы проверяете свои результаты с одинарным, а затем с целочисленными форматами int8 или int16 и подтверждаете, что они сопоставимы. Работа с целыми числами сложна, но в некоторых случаях она того стоит.
- Предостережение 1. Данные должны либо уже быть целыми числами в формате двойной точности, либо округляться до целых чисел без значительной потери информации (например, 1,001, 2,0045 и т. д.), либо вы должны быть готовы преобразовать свои данные. взад и вперед.
Чтобы увидеть, как это предостережение может навредить вам, если вы не будете осторожны, попробуйте следующее:
mydata = rand(10,10); mydata = int8( mydata ); mydata
❌ Вы получите кучу 0 и 1, потому что ваши данные были округлены.
Как мы можем этого избежать? Вы можете умножить каждое число на 10, 100 или 1000, а затем вычесть позже, если это необходимо.
% Scale up the data and scale it back down to_integer = @(x) x*1000 to_decimal = @(x) double(x)/1000 % Convert to integer mydata = int16( to_integer( rand(10,10) ) ); % And back to double! mydata = to_decimal( mydata );
- Предостережение 2. Ваши целые числа должны иметь установленное минимальное и максимальное значение, которое вы не превысите (и оно будет округлено до этого значения, если вы его превысите).
Если вы конвертируете с использованием int16
, это означает -2¹⁶ в 2¹⁶, поэтому вы потенциально можете хранить значения между -65536 и +65536 — или -6,5536 и +6,5536, чтобы иметь максимальную точность в ваших десятичных дробях — или между -65,636 и +65,636. Видишь, как это работает?
Каково максимальное значение матрицы
int8
? Прокрутите вниз, чтобы найти ответ.
Предупреждение 3. Вам нужно будет преобразовать в двойное или одинарное изображение, если вы хотите выполнять определенные виды обработки на своей матрице. Например, вы сможете взять среднее значение или стандартное отклонение вашей матрицы, но вы не сможете использовать inv
, например, для вычисления обратной матрицы.
Давайте проверим экономию от каждого типа данных
Мы попробуем с предположением, что наши данные будут меньше 256, и мы можем допустить потерю некоторых десятичных знаков (вы всегда должны проверять, что это работает для ваших данных).
to_integer = @(x) ( x*100 ) to_decimal = @(x) ( double(x) /100 ) mydata = rand(1000,1000); % 8 megabyte double mydata_to_integer = to_integer(mydata); % Still 8 megabytes (double) mydata_to_integer = single( mydata_to_integer ); % Now 4 megabytes % Check if the min and max are between -2^16 and 2^16 [ min(mydata_to_integer,[],'all'),max(mydata_to_integer,[],'all') ] mydata_to_integer = int16( mydata_to_integer ); % Now 2 megabytes % Since the max is 100, we can go down to int8 mydata_to_integer = int8( mydata_to_integer ); % 1 megabyte!
Теперь давайте попробуем восстановить наши исходные данные и посмотрим, насколько они точны, вычислив среднее значение:
format long mean( mydata, 'all' ) % Original data to_decimal( mean( mydata_to_integer,'all' ) ) % Converted back data
0,500068458752177 против 0,500070540000000. Давайте также вычислим стандартное отклонение.
std(mydata,[],'all') std( to_decimal( double( mydata_to_integer ) ),[],'all' )
0,288641641801215 против 0,288675051151625 — с этим можно жить.
Заключение
Для экономии памяти (оперативной памяти и памяти на жестком диске) следует использовать single(yourvariable)
. Это самый простой и простой способ сократить использование памяти вдвое. Если вы хотите сократить использование памяти до 90%, вам следует тщательно поэкспериментировать с int16
или int8
(которые хранят значения от -256 до +256, как ответ на вопрос выше).