2016-06-28 3 views
4

Я разрабатываю искусственную нейронную сеть в Objective-C, поэтому я написал несколько методов для арифметики вектор-матрица. Например, ниже приведен код вычисления внешнего продукта. Код работает нормально и возвращает желаемые результаты, но мой модульный тест не выполняется при сравнении возвращаемого методом объекта NSMutableArray с тем, который был создан в модульном тесте. Я потерялся на этом уже несколько дней. Кто-нибудь знает, почему XCTAssertEqualObjects() терпит неудачу, несмотря на то, что объекты кажутся одинаковыми?XCTAssertEqualObjects, сравнивающий NSArrays NSNumber, не работает, хотя массивы кажутся одинаковыми

Вот соответствующий код, чтобы вернуть внешнее произведение 2 векторов (NSArrays) в MLNNeuralNet.m:

-(NSMutableArray *)outerProduct:(NSArray *)matrix1 by:(NSArray *)matrix2 { 

/*Tensor Product of 2 vectors treated as column and row matrices, respectively*/ 

/*Example: if matrix1 is @[2, 4, 6] and matrix2 @[3, 4, 5], then calculation is: 
[2 * 3, 2 * 4, 2 * 5], [4 * 3, etc...] 
and result is: 
@[@[6, 8, 10], @[12, 16, 20], @[18, 24, 30]] 
*/ 

NSMutableArray *result = [[NSMutableArray alloc] init]; 

for (int i = 0; i < [matrix1 count]; i++) { 
    NSMutableArray *tempArray = [[NSMutableArray alloc] init]; 
    for (int j = 0; j < [matrix2 count]; j++) { 
     double product = [[matrix1 objectAtIndex:i] doubleValue] * [[matrix2 objectAtIndex:j] doubleValue]; 
     [tempArray addObject:@(product)]; 
    } 
    [result addObject:tempArray]; 
} 

return result; 
} 

А вот код для модульного тестирования:

@interface MLNNeuralNetTests : XCTestCase 

@property (strong, nonatomic) MLNNeuralNet *neuralNet; 

@end 

@implementation MLNNeuralNetTests 

- (void)setUp { 
    [super setUp]; 
    _neuralNet = [[MLNNeuralNet alloc] init]; 
} 

-(void)testOuterProduct { 

NSMutableArray *matrix1 = [[NSMutableArray alloc] initWithArray:@[@(1.0), @(2.0), @(3.0)]]; 
NSMutableArray *matrix2 = [[NSMutableArray alloc] initWithArray:@[@(4.2), @(5.2), @(6.2)]]; 

NSMutableArray *layer1 = [[NSMutableArray alloc] initWithArray:@[@(4.2), @(5.2), @(6.2)]]; 
NSMutableArray *layer2 = [[NSMutableArray alloc] initWithArray:@[@(8.4), @(10.4), @(12.4)]]; 
NSMutableArray *layer3 = [[NSMutableArray alloc] initWithArray:@[@(12.6), @(15.6), @(18.6)]]; 
NSMutableArray *correctMatrix = [[NSMutableArray alloc] 
           initWithArray:@[layer1, layer2, layer3]]; 

NSMutableArray *testMatrix = [self.neuralNet outerProduct:matrix1 by:matrix2]; 

XCTAssertEqualObjects(correctMatrix, testMatrix, @"Matrix outer product failed"); 
} 

И здесь ошибка я получаю:

Я думал это может быть из-за мое созданием литералов NSNumber в блок тестовой версии как @(4.2) etc...

поэтому я попробовал первое создание double с, а затем обертыванием в NSNumber так:

double number1 = 4.2; 
NSMutableArray *layer1 = [[NSMutableArray alloc] initWithArray:@[@(number1), etc... 

, но это тоже не сработало.

Я что-то упустил?

Когда я пытаюсь провести тестирование на равенство объектов в аналогичных тестах, у меня не возникло проблем. Например, следующий тест не прерывается:

-(void)testMultiplyVectorElements { 

    NSArray *vector1 = @[@(1.0), @(2.0), @(3.0), @(4.0)]; 
    NSArray *vector2 = @[@(5.2), @(6.2), @(7.2), @(8.2)]; 
    NSMutableArray *correctVector = [[NSMutableArray alloc] initWithArray:@[@(5.2), @(12.4), @(21.6), @(32.8)]]; 
    NSMutableArray *testVector = [self.neuralNet multiplyVectorElements:vector1 by:vector2]; 

    XCTAssertEqualObjects(correctVector, testVector, @"Vector element-wise multiplication failed."); 
} 

ответ

4

Я считаю, что это относится к арифметике с плавающей запятой. Сравнение с плавающей точкой может быть сложным. Результат их объединения не дает точно то, что вы ожидаете, если бы это были «реальные» числа.

Выход XCTAssertEqualObjects() использует NSLog() для печати NSNumber s, который округляет их для отображения. Вы можете вручную проверить и увидеть более точные значения:

NSUInteger row_idx = 0; 
for(NSArray<NSNumber *> * row in testMatrix){ 
    NSUInteger col_idx = 0; 
    for(NSNumber * testProduct in row){ 
     NSNumber * correctProduct = correctRow[row_idx][col_idx]; 
     NSLog(@"%.16lf %.16lf", [product doubleValue], correctProduct); 
     /* This level of accuracy fails with your code. Drop at 
     * least one 0 to pass the assertion. 
     */ 
     XCTAssertEqualWithAccuracy([product doubleValue], 
            [correctProduct doubleValue], 
            0.000000000000001); 
     col_number += 1; 
    } 
    row_number += 1; 
} 

Из этого вы можете видеть, что 12,6, что должно быть результатом умножения фактически 12,6000000000000014 и буквальным в correctMatrix 12.6 хранили в 12.5999999999999996. Поэтому они очень близки, но не ==.

Макрос XCTAssertEqualWithAccuracy() предназначен для сравнения значений с плавающей запятой. Он позволяет передать третье значение для создания диапазона, в пределах которого значения считаются «достаточно равными».

Другой вариант, если вы делаете много численным какао вычислений, чтобы переключиться на NSDecimalNumber, который делает дать точные «реальные» значения для арифметики. Компромисс в том, что это еще больший боль в шее, чем NSNumber, поскольку все операции проходят через методы. [x decimalNumberByMultiplyingBy:y].

+0

Спасибо за этот ответ, я боролся с этим. – anoop4real