Result mismatch of Desktop and Android for custom mobilenetv2 image classification

vinsol: <p><strong>Environment:</strong><br>
<em>Desktop:</em><br>
Pytorch:1.9.0+cu111<br>
<em>Android:</em><br>
pytorch_android_lite:1.9.0<br>
pytorch_android_torchvision:1.9.0</p>
<p>I customize MobileNetv2 Pretrained network for binary image classification.</p>
<p><code>model.classifier[1] = torch.nn.Linear(in_features=model.classifier[1].in_features, out_features=2)</code></p>
<p><em>I used transformation while prediction as follow:</em></p>
<pre><code class="lang-auto">transform = transforms.Compose([
    transforms.ToPILImage(),
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
  ])
</code></pre>
<p><em>Convert it to mobile deployable format - TorchScript as follow:</em></p>
<pre><code class="lang-auto">with torch.no_grad():
    model= torchvision.models.mobilenet_v2(pretrained=True)
    model.classifier[1] = torch.nn.Linear(in_features=model.classifier[1].in_features, out_features=2)
    m = torch.load(&lt;model.pth path&gt;, map_location=torch.device('cpu'))
    model.load_state_dict(m)
    model = torch.nn.Sequential(model,torch.nn.Softmax(1))
    model = model.to('cpu')
    model = model.eval()
    example = torch.rand(1, 3, 224, 224)
    q_model = torch.quantization.convert(model)
    model_script = torch.jit.trace(q_model,example)
    optimized_scripted_module = optimize_for_mobile(model_script)
    optimized_scripted_module._save_for_lite_interpreter('model.ptl')
</code></pre>
<p>I tried the mobile deployable model on the desktop and see the result. It is 99% matching, slightly varying in decimal point but It’s ok.</p>
<p><strong>I deployed it on Android but the results are not matching. It’s much different.</strong></p>
<pre><code class="lang-auto">Bitmap scaled_bitmap= Bitmap.createScaledBitmap(bitmap,224,224,false);
  // preparing input tensor
final Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(scaled_bitmap,
            TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, TensorImageUtils.TORCHVISION_NORM_STD_RGB, MemoryFormat.CONTIGUOUS);
final Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();
final float[] scores = outputTensor.getDataAsFloatArray();
</code></pre>
<p><strong>Desktop result:</strong> [0.9320468306541443, 0.06795316189527512]<br>
<strong>Android result:</strong> [0.30155882, 0.69844115 ]</p>
<blockquote>
<p>I tried with static input with all one. The result are matching.</p>
<p>Desktop result : [0.9261210560798645, 0.07387895882129669]<br>
Android result: [0.9261202,0.07387978]</p>
</blockquote>
<p>I think, there is something wrong with preprocessing of images. I am not sure pytorch transformation function and Android preprocessing function are too equivalent.</p>
<p>Any help regarding this?</p>

ptrblck: <p>Could you try to use a static input (e.g. <code>torch.ones</code>) and compare the outputs again? If they are matching, it would indicate that the data loading and processing might be the root cause. Otherwise the mismatch might be created in the model itself.</p>

vinsol: <p>I already tried with static input (e.g. <code>torch.ones</code>) with all one. The result are matching.</p>
<pre><code>Desktop result : [0.9261210560798645, 0.07387895882129669]
Android result: [0.9261202,  0.07387978]
</code></pre>
<p>I have followed <code>HelloWorldApp</code> example. Do we need extra preprocessing?</p>

ptrblck: <p>OK, this would indeed point to a data loading issue.<br>
I don’t know if extra preprocessing is needed, but would try to check if the inputs are interleaved, in another color format (BGR vs. RGB) etc.</p>

vinsol: <p>I can see the resize in 224 in Android degrade the quality of images. For comparison purpose, I checked it by resize and save images in Android then I tried to resize it on desktop, downloaded the image:</p>
<pre><code class="lang-auto">from torchvision import transforms
from PIL import Image
image = Image.open(&lt;path of image&gt;)
transform= transforms.Resize((224, 224))
img= transform(image)
display(img)
</code></pre>
<p>and tried the image which was saved with the desktop transformation on Android then the results are exactly matching.</p>
<p>Android code for resizing,<br>
<code>Bitmap inp_bitmap= Bitmap.createScaledBitmap(bitmap,224,224,false);</code></p>
<p>What will be a good image resize utility which is equivalent to the PyTorch transformation resize function?</p>
<p>Any help is appreciated.</p>

my3bikaht: <aside class="quote no-group" data-username="vinsol" data-post="1" data-topic="134672">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/v/45deac/48.png" class="avatar"> vinsol:</div>
<blockquote>
<p><code>transforms.Resize</code></p>
</blockquote>
</aside>
<p>Default resize mode for torchvision resize is bilinear,<br>
as for android function you mentioned, False means fast method, nearest neighborhood, and True is bilinear, slower one</p>

vinsol: <p>Thanks for reply,</p>
<p>I tried that option too.<br>
<code>Bitmap inp_bitmap= Bitmap.createScaledBitmap(bitmap,224,224,true);</code></p>
<p>The results are not that promising.</p>
<p>I just, tried Android Picasso and Glide library and applied resize and centerInside function The results are good but still not 100% accurate.</p>