2016-06-28 4 views
4

私はObjective-Cで人工ニューラルネットを開発していますので、行列ベクトル演算のいくつかのメソッドを記述しました。たとえば、外積計算のコードは次のとおりです。コードは正常に動作していて、目的の結果を返しますが、メソッド返されたNSMutableArrayオブジェクトを単体テストで作成されたオブジェクトと比較すると、単体テストが失敗します。私は数日間これで迷ってしまった。誰もがなぜXCTAssertEqualObjects()がオブジェクトが同一であると思われるにもかかわらず失敗するのを知っていますか?ここ配列が同じに見えても、NSNumberのNSArraysを比較するXCTAssertEqualObjectsは失敗します

はMLNNeuralNet.m 2つのベクター(NSArrays)の外積を返す関連するコードである:

-(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"); 
} 

そしてここで私は取得していますエラーは次のとおりです。

私は思いましたそれが原因私はので、私は最初double Sを作成してみましたし、このようNSNumberで包む@(4.2) etc...

のようなユニットテストバージョンでの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を印刷します。これは、表示のために丸めます。手動で検査し、より正確な値を見ることができます。このことから

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であることがわかります、と12.5999999999999996として保存したcorrectMatrix 12.6でリテラル。したがって、彼らは非常に近いですが、==ではありません。

XCTAssertEqualWithAccuracy()マクロは、浮動小数点値の比較用に設計されています。それは、値が "十分に等しい"とみなされる範囲を作成するために3番目の値を渡すことができます。

別のオプション、あなたは数値ココア・コンピューティングの多くをやっている場合は、は、正確な算術演算のための「本物」の値を与えるんれ、NSDecimalNumberに切り替えることです。すべての操作がメソッドを経由するので、首の痛みはさらにNSNumberよりも大きいというトレードオフです。 [x decimalNumberByMultiplyingBy:y]

+0

この回答をお寄せいただきありがとうございます。 – anoop4real

関連する問題