{"id":5550,"date":"2017-12-05T14:32:27","date_gmt":"2017-12-05T22:32:27","guid":{"rendered":"\/developerblog\/?p=5550"},"modified":"2020-03-20T09:20:19","modified_gmt":"2020-03-20T16:20:19","slug":"comparing-transfer-learning-systems-custom-vision-service-vs-inception-vs-mobilenet","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/comparing-transfer-learning-systems-custom-vision-service-vs-inception-vs-mobilenet\/","title":{"rendered":"Comparing Image-Classification Systems: Custom Vision Service vs. Inception"},"content":{"rendered":"<p>We recently worked with <a href=\"https:\/\/reverb.com\">Reverb<\/a>, an online marketplace for music gear. Musicians and music retailers use Reverb&#8217;s platform to buy and sell items such as guitars, keyboards, effects pedals, etc. A key element that aids discovery of items listed on the Reverb site is accurate metadata. For example, the manufacturer&#8217;s official name for the finish of a guitar\u00a0(e.g., &#8220;honey burst&#8221; or &#8220;fiesta red&#8221;) is an important criterion for surfacing relevant guitars in user searches. However, the finish name is sometimes omitted or not accurately entered when sellers list a guitar for sale.\u00a0To solve the problem of missing or mislabeled manufacturer finish metadata, we developed a set of image classification models with Reverb that help resolve manufacturer finish names based on product images.<\/p>\n<p>In this code story, we will cover how to build image classification models in Python with <a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\/custom-vision-service\/\">Custom Vision Service<\/a> and compare the results with popular Tensorflow-based models in terms of accuracy, prediction speed, training speed and setup complexity.<\/p>\n<h2>Getting started with the Custom Vision Service<\/h2>\n<p><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\/custom-vision-service\/\">Custom Vision Service<\/a> is a managed service that enables developers to easily customize state-of-the-art deep learning image classification models using transfer learning. You can read more about the service in this <a href=\"https:\/\/www.microsoft.com\/developerblog\/2017\/05\/12\/food-classification-custom-vision-service\/\">code story<\/a>. We decided to use this service for our solution to classify the manufacturer finish names because it provides a very quick getting-started experience and enables us to build image classification models without having to manage and scale deep learning infrastructure.<\/p>\n<p>A simple way to train models via the Custom Vision Service is to use its <a href=\"https:\/\/www.customvision.ai\/projects\">web portal<\/a> and create classifiers via a drag-and-drop user interface to upload and tag training images. However, this approach is inefficient when dealing with large amounts of training data and when iterating on new models and ideas since the process cannot be automated. As a result, we created a <a href=\"https:\/\/github.com\/CatalystCode\/py_custom_vision_client\">Python SDK<\/a> for the Custom Vision service. Using this SDK, it is straightforward to quickly train new models with large amounts of training data.<\/p>\n<p>To get started, install the Custom Vision Python SDK and its dependencies:<\/p>\n<pre class=\"lang:sh decode:true\">pip install custom_vision_client<\/pre>\n<p>We will assume your image data is laid out on disk using directories as labels like so:<\/p>\n<pre class=\"lang:default decode:true\">training-data\r\n\u251c\u2500\u2500 label1\r\n\u2502   \u251c\u2500\u2500 image1.jpg\r\n\u2502   \u2514\u2500\u2500 image2.jpg\r\n\u2514\u2500\u2500 label2\r\n    \u251c\u2500\u2500 image3.jpg\r\n    \u2514\u2500\u2500 image4.jpg\r\n<\/pre>\n<p>Now we\u2019re ready to build a model:<\/p>\n<pre class=\"lang:python decode:true\">from glob import glob\r\nfrom os.path import join\r\n\r\nfrom custom_vision_client import TrainingClient, TrainingConfig\r\n\r\nazure_region = \"southcentralus\"\r\ntraining_key = \"the-training-key\"  # from settings pane on customvision.ai\r\nproject_name = \"my-classifier\"\r\ndata_directory = \"\/path\/to\/training-data\"\r\n\r\ntraining_client = TrainingClient(TrainingConfig(azure_region, training_key))\r\n\r\n# create a new project, if the name already exists, a suffix will\r\n# be added to the name in order to make it unique\r\nproject_id = training_client.create_project(project_name).Id\r\n\r\nfor label in glob(data_directory):\r\n    images = glob(join(data_directory, label, '*.jpg'))\r\n    # register a new label if it doesn't already exist in the project\r\n    # also register training images, duplicates get ignored automatically\r\n    training_client.create_tag(project_id, label)\r\n    training_client.add_training_images(project_id, images, label)\r\n\r\nmodel_id = training_client.trigger_training(project_id).Id\r\n\r\nprint('{},{}'.format(project_id, model_id))  # record these for prediction\r\n<\/pre>\n<p>With Reverb, our objective was to classify an image into several categories of manufacturer finish names. These finish names roughly corresponded to colors and as such can be grouped naturally into distinct super-families like \u201cthe reds,\u201d \u201cthe greens,\u201d \u201cthe multi-colors\/sunburst,\u201d etc. We decided to utilize this natural structure of our data and build a hierarchical image classification model. First, we built a model to distinguish between the color super-families. Next, within each family, we built a second model to differentiate between the more detailed nuances of the finishes in that color family.<\/p>\n<p>The figure below shows a flow-chart diagram of the model:<\/p>\n<p><figure id=\"attachment_5552\" aria-labelledby=\"figcaption_attachment_5552\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model.png\" alt=\"Image reverb custom vision hierarchical model\" width=\"1547\" height=\"572\" class=\"aligncenter size-full wp-image-10844\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model.png 1547w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model-300x111.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model-1024x379.png 1024w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model-768x284.png 768w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-hierarchical-model-1536x568.png 1536w\" sizes=\"(max-width: 1547px) 100vw, 1547px\" \/><figcaption id=\"figcaption_attachment_5552\" class=\"wp-caption-text\">Hierarchical model for classifying color finish names for guitars<\/figcaption><\/figure><\/p>\n<p>First, we predicted high-level color families such as red, green or sunburst. Within each family we then trained an additional model to differentiate nuances; for example, to tell the difference between &#8220;bordeaux-metallic&#8221; and &#8220;fiesta-red&#8221; within the &#8220;red&#8221; family.<\/p>\n<p>The intention behind this model was to maximize the amount of training data available to the first classifier and enable the second level of classifiers to learn more discriminatory features within each color family instead of having a single model learning features that span the entire label space. For the Reverb data set, this approach increased precision to 87% and recall to 84% compared to a standard multi-label model with precision at 79% and recall at 75%. The downside of the tiered approach is that we now require two classifications per image at prediction time which doubles latency. A single prediction takes on the order of 200ms-400ms so the latency for the hierarchical model is on the order of 400ms-800ms.<\/p>\n<p><figure id=\"attachment_5553\" aria-labelledby=\"figcaption_attachment_5553\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-training-set-size.png\" alt=\"Image reverb custom vision performance training set size\" width=\"958\" height=\"530\" class=\"aligncenter size-full wp-image-10846\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-training-set-size.png 958w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-training-set-size-300x166.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-training-set-size-768x425.png 768w\" sizes=\"(max-width: 958px) 100vw, 958px\" \/><figcaption id=\"figcaption_attachment_5553\" class=\"wp-caption-text\">Chart showing the precision and recall of the Custom Vision Service as the amount of training data available to the model increases. We see that the service does well even with relatively limited amounts of training data.<\/figcaption><\/figure><\/p>\n<p>In synthesis, getting started building image classification models with the Custom Vision Service is very simple. Uploading images and building models can be done in a matter of minutes. No deep learning hardware needs to be set up or managed to train models. The Custom Vision Service leverages many data sets under the hood for transfer learning, so the performance of the trained models is strong, even with limited training data (see chart above). At prediction time, the latency for a request to the Custom Vision Service is on the order of 200ms to 400ms which is reasonable for many non-real-time applications (750ms to 1400ms if not in the same Azure data center). Note that the Custom Vision Service recently added support for <a href=\"https:\/\/azure.microsoft.com\/en-us\/blog\/custom-vision-service-introduces-classifier-export-starting-with-coreml-for-ios-11\/\">exporting models<\/a> to enable prediction to be run locally on edge devices, so this latency can be further reduced.<\/p>\n<h2>Getting started with Inception V3<\/h2>\n<p>In order to get an idea how Custom Vision Service compares in both prediction accuracy and speed to other popular convolutional networks, we built a pair of transfer learning implementations based on Inception V3 and MobileNet. For simplicity, the examples below are based on the command line tools included in TensorFlow&#8217;s examples.<\/p>\n<p>Both Inception V3 and MobileNet networks were retrained using the <a href=\"https:\/\/hub.docker.com\/r\/tensorflow\/tensorflow\/tags\/\">tensorflow\/tensorflow:1.3.0-devel-py3 Docker image<\/a>.\u00a0All experiments were done in a CPU-centric environment to mirror a production service deployment environment (that is, without Nvidia-Docker or GPU support due to the cost of running these in production at scale). From a working directory which includes the training images, you can start by running the following Docker container:<\/p>\n<pre class=\"lang:sh decode:true\">docker run --rm -it -v `pwd`:\/c13n -w \/c13n tensorflow\/tensorflow:1.3.0-devel-py3 bash<\/pre>\n<p>Assuming the training images are laid out as described above, training can be done using the <a href=\"https:\/\/github.com\/tensorflow\/tensorflow\/blob\/r1.3\/tensorflow\/examples\/image_retraining\/retrain.py\">retrain.py<\/a> script like so:<\/p>\n<pre class=\"lang:sh decode:true\">python3 \/tensorflow\/tensorflow\/examples\/image_retraining\/retrain.py \\\r\n    --architecture inception_v3 \\\r\n    --image_dir \/c13n\/training-data \\\r\n    --output_graph \/c13n\/index.pb \\\r\n    --output_labels \/c13n\/index.txt<\/pre>\n<p>After a few minutes, the script should complete with output similar to the following:<\/p>\n<pre class=\"lang:default decode:true\">INFO:tensorflow: Step 3999: Train accuracy = 79.0%\r\nINFO:tensorflow: Step 3999: Cross entropy = 1.548623\r\nINFO:tensorflow: Step 3999: Validation accuracy = 61.0% (N=100)\r\nINFO:tensorflow:Final test accuracy = 65.6% (N=570)\r\n<\/pre>\n<p>As you can see from the example, at 61.0% accuracy, the performance of Inception V3 trained on 5000 images is comparable to that of the Custom Vision Service trained on 200 images. Retraining of the Inception V3 neural network can take somewhere between 6-15 minutes per model whereas the Custom Vision Service only takes between 10 seconds to 5 minutes to train depending on the dataset size (timed on a 2.9 GHz Intel Core i7 machine with 16GB of RAM).<\/p>\n<p>In order to run a prediction using this newly-trained model we can run\u00a0the following command from TensorFlow&#8217;s examples:<\/p>\n<pre class=\"lang:default decode:true\">python3 \/tensorflow\/tensorflow\/examples\/label_image\/label_image.py \\\r\n    --image \/path\/to\/image.jpg \\\r\n    --input_width 128 \\\r\n    --input_height 128 \\\r\n    --graph \/c13n\/red.pb \\\r\n    --labels \/c13n\/red.txt \\\r\n    --input_layer DecodeJpeg \\\r\n    --output_layer final_result<\/pre>\n<p>Running a prediction using a local Inception V3 and a local image takes significantly longer (6000ms to 7000ms) than running the equivalent request on Custom Vision&#8217;s REST endpoint (750ms to 1400ms). We can shave about 1000ms off the prediction time by wrapping the Inception prediction code in a REST service (e.g. using <a href=\"http:\/\/www.hug.rest\">hug<\/a>, shown in this\u00a0<a href=\"https:\/\/github.com\/reverbdotcom\/reverb-color\/blob\/e9e723ab96ddfefe104f29f4b7da0d10069f5887\/inception\/predict.py#L100\">sample code<\/a>) and only loading the Inception model once at service start instead of for every prediction.<\/p>\n<h2>Getting started with MobileNet<\/h2>\n<p>The steps for retraining and using a MobileNet model are almost identical as the those for Inception V3. The only difference is the value passed through the <span class=\"lang:sh decode:true crayon-inline \">&#8211;architecture<\/span>\u00a0 command line parameter:<\/p>\n<pre class=\"lang:default decode:true\">python3 \/tensorflow\/tensorflow\/examples\/image_retraining\/retrain.py \\\r\n    --architecture mobilenet_0.25_128_quantized \\\r\n    --image_dir \/c13n\/training-data \\\r\n    --output_graph \/c13n\/index.pb \\\r\n    --output_labels \/c13n\/index.txt<\/pre>\n<p>Interestingly, running the training on the same set of 5,500 training images of size 128&#215;128, the accuracy of the MobileNet model increases to 72.5%, higher than the one for Inception V3 (likely with some overfitting in MobileNet as hinted-at by the very high training accuracy).<\/p>\n<pre class=\"lang:default decode:true\">INFO:tensorflow: Step 3999: Train accuracy = 99.0%\r\nINFO:tensorflow: Step 3999: Cross entropy = 0.112992\r\nINFO:tensorflow: Step 3999: Validation accuracy = 75.0% (N=100)\r\nINFO:tensorflow:Final test accuracy = 72.5% (N=570)<\/pre>\n<p>However,\u00a0based on the community at large&#8217;s empirical experience, it&#8217;s likely that the performance of a MobileNet model on other data sets will be lower relative to Inception V3 since MobileNet is optimized for speed whereas Inception aims for correctness.<\/p>\n<p>Another noteworthy difference between Inception and MobileNet is the big savings in model size at 900KB for MobileNet vs 84MB for Inception V3. You can experiment further by switching between variants of MobileNet. For instance, using <span class=\"lang:sh decode:true crayon-inline \">mobilenet_1.0_128<\/span>\u00a0 as the base model increases the model size to 17MB but also increases accuracy to 80.9%.<\/p>\n<pre class=\"lang:default decode:true\">INFO:tensorflow: Step 3999: Train accuracy = 100.0%\r\nINFO:tensorflow: Step 3999: Cross entropy = 0.034551\r\nINFO:tensorflow: Step 3999: Validation accuracy = 87.0% (N=100)\r\nINFO:tensorflow:Final test accuracy = 80.9% (N=570)\r\n<\/pre>\n<p>It&#8217;s also worth mentioning that this smaller model size is accompanied with a shorter prediction runtime of 950ms versus one of roughly 5000ms with Inception V3. If we wrap the MobileNet model in a REST service and load model only once during startup, one can achieve prediction speeds of around 400ms.<\/p>\n<p>For running prediction, the\u00a0<a href=\"https:\/\/github.com\/tensorflow\/tensorflow\/blob\/57b1c56214e88fc3b00f6ff518cb3277bfeb660a\/tensorflow\/examples\/image_retraining\/label_image.py\">label_image.py<\/a> script can be invoked for a MobileNet model as shown below. (Note that the input_layer and output_layer args are different than those used for Inception V3.)<\/p>\n<pre class=\"lang:default decode:true\">time python3 \/tensorflow\/tensorflow\/examples\/label_image\/label_image.py \\\r\n    --image \/path\/to\/image.jpg \\\r\n    --input_width 128 \\\r\n    --input_height 128 \\\r\n    --graph \/c13n\/red.mobilenet.pb \\\r\n    --labels \/c13n\/red.mobilenet.txt \\\r\n    --input_layer input \\\r\n    --output_layer final_result\r\n\r\nbordeaux metallic 0.999991\r\nburgundy mist metallic 8.92877e-06\r\nburgundy mist 1.87918e-07\r\ndakota red 4.31156e-09\r\n\r\nreal 0m0.955s\r\nuser 0m0.840s\r\nsys 0m0.190s\r\n<\/pre>\n<h2>Framework comparison<\/h2>\n<p>The figures below show a comparison of the predictions of the Custom Vision Service, Inception and MobileNet models on a variety of sample guitar images with diverse rotations, backgrounds, lighting and cropping factors. Overall, MobileNet is the fastest model at prediction time and the Custom Vision Service is the most accurate while having an acceptable runtime.<\/p>\n<p><figure id=\"attachment_5554\" aria-labelledby=\"figcaption_attachment_5554\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-comparison.png\" alt=\"Image reverb custom vision performance comparison\" width=\"1453\" height=\"1068\" class=\"aligncenter size-full wp-image-10845\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-comparison.png 1453w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-comparison-300x221.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-comparison-1024x753.png 1024w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/reverb-custom-vision-performance-comparison-768x565.png 768w\" sizes=\"(max-width: 1453px) 100vw, 1453px\" \/><figcaption id=\"figcaption_attachment_5554\" class=\"wp-caption-text\">Sample guitar images alongside their color finish name as predicted by the Tensorflow-based MobileNet and Inception models as well as the predictions made by the Custom Vision Service. We see that the Custom Vision Service does well across a wide range of guitar images, rotations, partial views, lighting changes, etc.<\/figcaption><\/figure><\/p>\n<p><figure id=\"attachment_6424\" aria-labelledby=\"figcaption_attachment_6424\" class=\"wp-caption aligncenter\" ><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart.png\" alt=\"Image model comparison chart\" width=\"2474\" height=\"1363\" class=\"aligncenter size-full wp-image-10843\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart.png 2474w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart-300x165.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart-1024x564.png 1024w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart-768x423.png 768w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart-1536x846.png 1536w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2017\/12\/model-comparison-chart-2048x1128.png 2048w\" sizes=\"(max-width: 2474px) 100vw, 2474px\" \/><figcaption id=\"figcaption_attachment_6424\" class=\"wp-caption-text\">Charts comparing the Tensorflow-based models MobileNet and Inception with the Custom Vision Service in terms of accuracy, model size, training time and prediction time. We see that the Custom Vision Service has the best aggregate performance across the categories.<\/figcaption><\/figure><\/p>\n<h2>Summary<\/h2>\n<p>In this code story, we&#8217;ve shown how to tackle a custom image classification task via transfer learning on the Custom Vision Service and Tensorflow. Overall, we found the Custom Vision Service to have the best performance on our task. Due to the high-end models backing the service, we expect the great performance can be generalized to other image classification tasks such as identifying sign language utterances in a video stream or classifying skin disease based on smartphone camera pictures.<\/p>\n<p>The Custom Vision Service offers a simple getting-started scenario enabling us to build models in minutes via a simple <a href=\"https:\/\/github.com\/CatalystCode\/py_custom_vision_client\">Python SDK<\/a> without having to set up a complicated Tensorflow stack. If you&#8217;re interested in trying out the Custom Vision Service to get started with your image classification project, take a look at the\u00a0<a href=\"https:\/\/github.com\/CatalystCode\/py_custom_vision_client\">Python SDK<\/a>\u00a0and <a href=\"https:\/\/github.com\/CatalystCode\/py_custom_vision_client\/issues\/new\">get in touch<\/a> with us on GitHub! We&#8217;d love to hear your feedback.<\/p>\n<h2>Resources<\/h2>\n<ul>\n<li><a href=\"https:\/\/azure.microsoft.com\/en-us\/services\/cognitive-services\/custom-vision-service\/\">Custom Vision Service documentation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/CatalystCode\/py_custom_vision_client\">Custom Vision Service SDK (Python)<\/a><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This story covers how to get started with transfer-learning and build image classification models in Python with the Custom Vision Service. We compare the results with the popular Tensorflow-based models Inception and MobileNet.<\/p>\n","protected":false},"author":21408,"featured_media":10842,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[14,19],"tags":[139,147,239,250,350],"class_list":["post-5550","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cognitive-services","category-machine-learning","tag-custom-vision-service","tag-deep-learning","tag-machine-learning-ml","tag-microsoft-cognitive-services","tag-tensorflow"],"acf":[],"blog_post_summary":"<p>This story covers how to get started with transfer-learning and build image classification models in Python with the Custom Vision Service. We compare the results with the popular Tensorflow-based models Inception and MobileNet.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/5550","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/21408"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=5550"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/5550\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/10842"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=5550"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=5550"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=5550"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}