<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PyCharm : The only Python IDE you need. | The JetBrains Blog</title>
	<atom:link href="https://blog.jetbrains.com/pycharm/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.jetbrains.com</link>
	<description>Developer Tools for Professionals and Teams</description>
	<lastBuildDate>Mon, 31 Aug 2026 13:50:33 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.jetbrains.com/wp-content/uploads/2024/01/cropped-mstile-310x310-1-32x32.png</url>
	<title>PyCharm : The only Python IDE you need. | The JetBrains Blog</title>
	<link>https://blog.jetbrains.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Fine-Tuning SOTA Object Detection Models on Real-World Datasets</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/fine-tuning-sota-object-detection-models-on-real-world-datasets/</link>
		
		<dc:creator><![CDATA[Arina Belova]]></dc:creator>
		<pubDate>Mon, 31 Aug 2026 13:50:29 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-social-BlogFeatured-1280x720-1-2.png</featuredImage>		<category><![CDATA[data-science]]></category>
		<category><![CDATA[object-detection]]></category>
		<category><![CDATA[yolo]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=732973</guid>

					<description><![CDATA[In our previous blog post in this series, we discussed state-of-the-art models for object detection: the architectures, the theory, and what makes YOLO12, YOLO26, and RF-DETR tick. If you want the theoretical background on these models, start there. This post is the practical follow-up: how to actually use these models, how to fine-tune them on [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>In our <a href="https://blog.jetbrains.com/pycharm/2026/07/best-object-detection-models-for-machine-learning-in-2026/">previous blog post</a> in this series, we discussed state-of-the-art models for object detection: the architectures, the theory, and what makes YOLO12, YOLO26, and RF-DETR tick. If you want the theoretical background on these models, start there.</p>



<p>This post is the practical follow-up: how to actually <strong>use</strong> these models, how to <strong>fine-tune</strong> them on diverse, specialized datasets that look nothing like their training data, and how to <strong>evaluate</strong> the results – all within PyCharm.</p>



<h2 class="wp-block-heading">Why fine-tune at all?</h2>



<p>Every pretrained detector you download was trained on some distribution of images, almost always <a href="https://cocodataset.org/" target="_blank" rel="noopener">COCO</a>, which is ~118k training images of everyday scenes containing 80 common object categories (people, cars, dogs, chairs, etc.).</p>



<p>Real-world deployment data rarely looks like COCO. Things that object detection might actually be applied to, such as damaged industrial cables, bone fractures on X-rays, or densely stacked soda bottles on a shelf, are:</p>



<ul class="wp-block-list">
<li><strong>Out of vocabulary</strong>: &#8220;Bone fracture&#8221; is not one of COCO&#8217;s 80 classes, so the model literally has no output category for it.</li>



<li><strong>Out of visual distribution</strong>: X-ray imagery, industrial close-ups, and heavily occluded shelf scenes differ drastically from consumer photos in texture, viewpoint, and object density.</li>
</ul>



<p>Deploying a detector on off-distribution data therefore requires <strong>fine-tuning</strong>. But before we break the models, let&#8217;s establish that we get similar results on our hardware to the ones reported by developers.</p>



<h2 class="wp-block-heading">The models</h2>



<p>For the purposes of this experiment, we’ll focus on three current SOTA object detection families and examine two sizes of each model:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Family</strong></th><th><strong>Variants</strong></th><th><strong>Implementation</strong></th></tr></thead><tbody><tr><td>YOLO12</td><td><code>yolov12n, yolov12m</code></td><td><a href="https://github.com/sunsmarterjie/yolov12" target="_blank" rel="noopener">Original authors&#8217; repo</a></td></tr><tr><td>YOLO26</td><td><code>yolo26n, yolo26m</code></td><td><a href="https://github.com/ultralytics/ultralytics" target="_blank" rel="noopener">Ultralytics</a> PyPI package</td></tr><tr><td>RF-DETR</td><td><code>RFDETRNano, RFDETRBase</code></td><td><a href="https://github.com/roboflow/rf-detr" target="_blank" rel="noopener">Roboflow</a> PyPI package</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">Sanity check: Reproducing COCO <code>val2017</code> baselines</h3>



<p>We’re going to be working with six pretrained checkpoints: Two different sizes of each of the three models. To check that these models are behaving as expected, we evaluated all of them on the full 5,000-image COCO validation dataset (<code>val2017</code>) to verify the numbers reported in the previous post:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Model</strong></th><th><strong>Params (M)</strong></th><th><strong>mAP50</strong></th><th><strong>mAP50-95</strong></th><th><strong>Latency (ms)</strong></th></tr></thead><tbody><tr><td>YOLOv12-N</td><td>2.55</td><td>0.5548</td><td>0.4021</td><td>23.9</td></tr><tr><td>YOLO26-N</td><td>2.57</td><td>0.5498</td><td>0.3952</td><td><strong>12.3</strong></td></tr><tr><td>YOLOv12-M</td><td>19.67</td><td>0.6953</td><td><strong>0.5259</strong></td><td>72.4</td></tr><tr><td>YOLO26-M</td><td>21.90</td><td>0.6906</td><td>0.5181</td><td>13.9</td></tr><tr><td>RF-DETR Nano</td><td>30.47</td><td>0.6750</td><td>0.4835</td><td><strong>12.4</strong></td></tr><tr><td>RF-DETR Base</td><td>32.17</td><td>0.7210</td><td><strong>0.5325</strong></td><td>12.9</td></tr></tbody></table></figure>



<p>Three things stand out even before we leave COCO behind:</p>



<ol class="wp-block-list">
<li><strong>Larger models (mostly) have better performance.</strong> RF-DETR Base leads (0.5325 mAP50-95), but the medium YOLOs get remarkably close (0.5259 / 0.5181) with ~10M fewer parameters.</li>



<li><strong>YOLO26&#8217;s NMS-free design pays off in throughput.</strong> YOLO26-N is the fastest model in the lineup (12.3 ms latency) at essentially the same mAP50-95 as YOLOv12-N, which, despite being the smallest model here, is considerably slower (23.9 ms latency). Attention is expensive. (If you want more detail on the model architectures and how they affect performance, see the <a href="https://blog.jetbrains.com/pycharm/2026/07/best-object-detection-models-for-machine-learning-in-2026/">previous blog post</a> in this series.)</li>



<li><strong>RF-DETR Nano is not &#8220;nano&#8221; by parameter count</strong> (~30M – more than YOLO26-M), but it is well-optimized: With 12.4 ms latency, it is the second-fastest overall.</li>
</ol>



<p>Published papers report <em>optimized</em> inference latency: That is, they measure the model&#8217;s forward pass in isolation, stripped of the surrounding stages of the object detection pipeline. We deliberately skipped that aggressive optimization so our numbers reflect what you&#8217;d actually see when deploying these models.</p>



<p>As a result, our latency figures don&#8217;t line up with the benchmarks in the models&#8217; white papers. There are two main reasons for this:</p>



<ul class="wp-block-list">
<li><strong>Hardware:</strong> We used different hardware from the NVIDIA T4 GPU that serves as the <em>de facto</em> standard in object detection benchmarking.</li>



<li><strong>Unoptimized computation graph:</strong> We ran the models in their native framework rather than converting them to TensorRT. TensorRT compiles a network into a hardware-specific engine, fusing layers, selecting the fastest kernels for the target GPU, and optionally running in reduced precision. That can cut latency substantially, but the resulting engine is tied to one GPU and requires an extra build step, so it doesn&#8217;t represent how these models perform out of the box.</li>
</ul>



<p>Accuracy is a different story: While our latencies diverge from the published ones, our mAP50-95 results fall within reasonable noise bounds of the reported figures.</p>



<p>Now that we’ve seen what our pretrained models can do on COCO, the dataset they were trained on, let&#8217;s see what happens when they&#8217;re tested off distribution.</p>



<h2 class="wp-block-heading">The datasets</h2>



<p>For evaluation, we used <a href="https://rf100-vl.org/" target="_blank" rel="noopener">RF100-VL</a>, a large-scale collection of 100 multimodal datasets covering concepts deliberately chosen to be rare in object detection models’ pretraining data. These datasets contain exactly the off-distribution targets we care about. These targets also mirror common real-life applications for object detection, giving us a realistic test of these models’ capabilities out in the wild.</p>



<p>We picked three datasets that stress test the models in different ways:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Dataset</strong></th><th><strong>Domain</strong></th><th><strong>Why it&#8217;s hard</strong></th><th><strong>Classes</strong></th></tr></thead><tbody><tr><td><strong><code>cable-damage</code></strong></td><td>Technical/industrial</td><td>Fine-grained damage types on visually similar backgrounds</td><td><code>break, thunderbolt</code></td></tr><tr><td><strong><code>bone-fracture</code></strong></td><td>Medical (X-ray)</td><td>Entirely different imaging modality; subtle features</td><td><code>angle, fracture, line, messed_up_angle</code></td></tr><tr><td><strong><code>soda-bottles</code></strong></td><td>Retail</td><td>Heavy occlusion, many near-identical instances per image</td><td><code>coca-cola, fanta, sprite</code></td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Tutorial: Fine-tuning all three models in PyCharm 🙂</h2>



<h3 class="wp-block-heading">Step 1: Setting up the project</h3>



<p>One of the first challenges we had to overcome in this project was that the three implementations do <strong>not</strong> share a compatible set of dependencies. In particular, the two different generations of YOLO require different versions of the <code>ultralytics</code> package. PyCharm offers a clean solution for this: one PyCharm project with three isolated <code>uv</code> environments – one per model family.</p>



<p>We&#8217;ll run our computations on a remote GPU. Configuring a remote interpreter in PyCharm follows the same workflow as a local one: the same dialog and the same dropdown as in the local case. Note that remote interpreters require PyCharm Professional; Community Edition supports local environments only.</p>



<p>Firstly, we need to instantiate our three <code>uv</code> virtual environments via:<br></p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">cd yolov12 &amp;&amp; uv venv .venv --python 3.11 

cd yolov26 &amp;&amp; uv venv .venv --python 3.11 

cd rf-detr &amp;&amp; uv venv .venv --python 3.11</pre>



<p>Once your uv virtual environments exist, register each one as an existing interpreter. Go to <em>Settings | Python | Interpreter</em>, click <em>Add Interpreter → Add Local Interpreter</em>, choose <em>Environment </em>as <em>Select existing</em>, and point the interpreter field at that environment&#8217;s <code>bin/python</code>. PyCharm doesn&#8217;t create anything here, it just picks up the environment <code>uv</code> already built.</p>



<p>Repeat for each environment. From then on, switching is a matter of picking one from the <em>Settings | Python | Interpreter</em> dropdown, or from the interpreter widget in the bottom-right-hand status bar.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" fetchpriority="high" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/uv_virtual_env.png" alt="" class="wp-image-733186"/></figure>



<p>You can find the full list of dependencies required for each model in their respective project repositories. You can either install all the projects’ dependencies in PyCharm’s built-in <em>Terminal</em> tool window or install individual packages using the<em> Python Packages</em> tool window (including selecting specific versions of packages). You can access both of these tool windows by clicking the relevant icons in the lower left-hand corner of the PyCharm toolbar.&nbsp;</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Screenshot-2026-08-14-at-16.29.31-1.png" alt="" class="wp-image-733197"/></figure>



<p>For a step-by-step guide on setting up the environments for all three models, see our <a href="https://github.com/ArinaBelova/sota-object-detection-tutorial/blob/main/environments_setup.md" target="_blank" rel="noopener">GitHub implementation</a> of this tutorial.</p>



<h3 class="wp-block-heading">Step 2: Getting the datasets</h3>



<p>To obtain the out-of-COCO-distribution datasets, we can install our datasets via the <code>rf-detr</code> virtual environment, since it has <code>roboflow</code> as one of its core dependencies. We then set the Roboflow API key as an environment variable so that it is available to the API when downloading the datasets.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="shell" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">pip install roboflow

export ROBOFLOW_API_KEY="your_key_here" # you can get API key here: https://docs.roboflow.com/reference/authentication/authentication/find-your-roboflow-api-key </pre>



<p>After setting everything up, now you can run the Python script below to get the three datasets we’re going to use in our tutorial:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import os
from roboflow import Roboflow

api_key = os.environ.get("ROBOFLOW_API_KEY")

if not api_key:
    raise RuntimeError("ROBOFLOW_API_KEY is not set")

DATASETS = [
    "bone-fracture-7fylg",
    "cable-damage",
    "soda-bottles",
]

VERSION = 2          # RF100 projects are generally published at version 2
FORMAT = "yolov8"    # or "coco", "voc", "yolov5"
rf = Roboflow(api_key=api_key)
workspace = rf.workspace("rf100")

for slug in DATASETS:
    print(f"Downloading {slug} ...")

    try:
        project = workspace.project(slug)
        dataset = project.version(VERSION).download(FORMAT)
        print(f"  -> {dataset.location}")

    except Exception as e:
        print(f"  !! failed: {e}")</pre>



<p>This script connects to the Roboflow cloud service via its Python API client and downloads three specified RF100 datasets in YOLOv8 format. It loops through each dataset, reports where successful downloads are saved, and prints an error if any download fails.</p>



<h3 class="wp-block-heading">Step 3: Getting a zero-shot baseline by using pretrained models on custom data</h3>



<p>Before fine-tuning, we’re going to evaluate the COCO-pretrained checkpoints directly on our three datasets, to see whether the fine-tuning is actually necessary. The result was unambiguous: <strong>The models predicted essentially nothing.</strong></p>



<p><em>Zero-shot mAP50-95 on the test splits of our three datasets:</em></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Model</strong></th><th><strong><code>cable-damage</code></strong></th><th><strong><code>bone-fracture</code></strong></th><th><strong><code>soda-bottles</code></strong></th></tr></thead><tbody><tr><td>RF-DETR Nano</td><td>0.0004</td><td>0.0000</td><td>0.0027</td></tr><tr><td>RF-DETR Base</td><td>0.0005</td><td>0.0000</td><td>0.0004</td></tr><tr><td>YOLOv12-N</td><td>0.0007</td><td>0.0000</td><td>0.0266</td></tr><tr><td>YOLO26-N</td><td>0.0000</td><td>0.0000</td><td>0.0033</td></tr><tr><td>YOLOv12-M</td><td>0.0000</td><td>0.0000</td><td>0.0160</td></tr><tr><td>YOLO26-M</td><td>0.0000</td><td>0.0000</td><td>0.0012</td></tr></tbody></table></figure>



<p>This is to be expected; it’s not a bug! As the models are closed-vocabulary detectors, that is, they have a finite number of predefined target classes, they physically cannot output a class like <code>fracture</code> that isn&#8217;t in their 80-class COCO head.&nbsp;</p>



<p>This is the punchline of this whole post: A model scoring 0.72 mAP50 on COCO scores 0.00 on bone fractures. Pretrained ≠ deployable, even when the model is state of the art. Basic machine learning principles still apply, even in the age of AI!</p>



<h3 class="wp-block-heading">Step 4: Fine-tuning</h3>



<p>All models were fine-tuned on a single <strong>A100 GPU</strong> for <strong>10 epochs</strong>. We used standard Ultralytics/RF-DETR fine-tuning pipelines in order to fine-tune the models on our three datasets. We fine-tuned a model for each dataset. The full fine-tuning pipeline can be found in <code>finetune_rf100.py</code> scripts in the project repo, under the folders for each model.</p>



<p>You can see the core of the training setup below. Both YOLO and RF-DETR are built on PyTorch under the hood, but the training loops are abstracted behind higher-level library APIs: Ultralytics’ <code>YOLO.train()</code> for the YOLO models, and RF-DETR’s own <code>train()</code> functionality.</p>



<h4 class="wp-block-heading">YOLO12 and YOLO26</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">train_model = YOLO(args.model)

train_res = train_model.train(
                data=str(yaml_path),
                epochs=args.epochs,
                imgsz=args.imgsz,
                batch=args.batch,
                device=args.device,
                project=args.project,
                name=run_name,
                exist_ok=True,
                verbose=False,
            )</pre>



<h4 class="wp-block-heading">RF-DETR</h4>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">ModelClass().train(
                dataset_dir=str(coco_dir),
                output_dir=str(output_dir),
                epochs=args.epochs,
                batch_size=args.batch_size,
                grad_accum_steps=args.grad_accum,
                lr=args.lr,
                resolution=resolution,
                early_stopping=True,
                checkpoint_interval=1,
            )</pre>



<h3 class="wp-block-heading">Step 5: Results</h3>



<p>Fine-tuning transforms the picture. You can see the results on the test set after training:</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/accuracy_vs_speed.png" alt="" class="wp-image-733209"/></figure>



<p>On the left, we have the pretrained models’ results for the COCO validation dataset. As we showed earlier, accuracy (mAP50-95) fell between 0.39 and 0.53, and all models except for YOLOv12-M showed low latency. The fine-tuned models on the right showed a similar range of accuracy for the <code>cable-damage</code> and <code>soda-bottle</code> detection tasks, only falling lower for the <code>bone-fracture</code> task. Moreover, the fine-tuned models were comparable in latency to the pretrained models for their intended tasks, and for YOLOv12-M, they were even faster. This suggests that, after fine-tuning to the target domain, the models achieve performance that’s broadly comparable to the pretrained performance on their original training domain.</p>



<p>Let’s now have a closer look at the fine-tuned models’ performance, breaking it down by mAP50 and mAP50-95 for the three separate RF-100 datasets:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th><strong>Model</strong></th><th><strong><code>cable-damage</code></strong></th><th><strong><code>bone-fracture</code></strong></th><th><strong><code>soda-bottles</code></strong></th></tr></thead><tbody><tr><td>RF-DETR Nano</td><td>0.9195 (0.4391)</td><td>0.2317 (0.1136)</td><td>0.9617 (0.6223)</td></tr><tr><td>RF-DETR Base</td><td><strong>0.9281</strong> (<strong>0.4456</strong>)</td><td><strong>0.4474</strong> (<strong>0.1915</strong>)</td><td>0.9688 (0.6332)</td></tr><tr><td>YOLOv12-N</td><td>0.9236 (0.4378)</td><td>0.0911 (0.0532)</td><td>0.9677 (0.6343)</td></tr><tr><td>YOLO26-N</td><td>0.8165 (0.3681)</td><td>0.0193 (0.0064)</td><td>0.9148 (0.5896)</td></tr><tr><td>YOLOv12-M</td><td>0.8266 (0.3649)</td><td>0.1500 (0.0635)</td><td><strong>0.9706</strong> (<strong>0.6422</strong>)</td></tr><tr><td>YOLO26-M</td><td>0.8707 (0.3896)</td><td>0.2194 (0.1038)</td><td>0.9596 (0.6304)</td></tr></tbody></table></figure>



<p>What the numbers say:</p>



<ul class="wp-block-list">
<li><strong>The <code>soda-bottles</code> target is the easy win.</strong> Every model lands in the 0.91–0.97 mAP50 band. This is likely due to the fact that the domain (consumer products in photos) is visually close to existing classes in COCO, so only the vocabulary was new. Interestingly, the attention model family does great here, with YOLOv12-M taking the top spot (0.6422 mAP50-95).</li>



<li><strong><code>cable-damage</code>: Detection is easy, but localization is hard.</strong> mAP50 reaches 0.93, but mAP50-95 tops out at 0.446. It appears that the models find the damage reliably, yet they struggle to box thin, elongated defects precisely. If your application needs tight boxes at high IoU, this gap would be a significant issue.</li>



<li><strong><code>bone-fracture</code> remains genuinely hard.</strong> The best model (RF-DETR Base, 0.447 mAP50) is far from production-ready, and the performance spread across models is huge. The modality shift from photos to X-rays means the pretrained backbone features transfer poorly. The different image modality and small, sometimes almost indistinguishable bone fractures make the detection task way harder than the one employed on common objects identification. This is the dataset that would most benefit from domain-specific pretraining, more data, or longer fine-tuning.&nbsp;</li>



<li><strong>RF-DETR Base is the most consistent performer</strong>, winning on two out of three datasets and challenging seriously for the third. The DETR-style architecture seems to transfer more robustly to unfamiliar domains.&nbsp;</li>
</ul>



<h3 class="wp-block-heading">Qualitative results</h3>



<p>To visually assess how these models perform, we can overlay the predicted bounding boxes on the images. Let&#8217;s look at the objects our models detected in six random images per class:</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/cable-damage_very_low_res.png" alt="" class="wp-image-733331"/></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/bone-fracture-7fylg.png" alt="" class="wp-image-733256"/></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/soda-bottles_very_low_res.png" alt="" class="wp-image-733320"/></figure>



<p>We can see this confirms the accuracy values we saw above: The noisy images of soda bottles in fridges are labeled accurately, with tight bounding boxes for each object. The cable damage is identified less consistently, with some models failing to find the damage altogether, and others creating unnecessarily large bounding boxes. Finally, the images of broken bones contrast sharply with the other two, with less than half of the images having any break identified, and different models identifying different potential breakage points.</p>



<h2 class="wp-block-heading">Conclusions&nbsp;</h2>



<p>Pretrained object detectors are powerful, based on advancements in model architecture over the past five years, but as we’ve seen here, pretrained does not necessarily mean deployable. All six models performed well on COCO, yet when we applied those same checkpoints directly to our specialized datasets, their performance fell close to zero. However, fine-tuning completely changed that picture.</p>



<p>After only 10 epochs of fine-tuning, all three model families were able to adapt well to both the <code>cable-damage</code> and <code>soda-bottle</code> datasets. As we noted, the <code>soda-bottle</code> task was particularly transferable, likely because it contained objects similar to those contained in COCO. <code>cable-damage</code> was also detected relatively reliably, although the larger gap between mAP50 and mAP50-95 showed that precisely locating these tiny defects was still challenging for all of the models. However, <code>bone-fracture</code> was a completely different story, likely because moving from the sort of natural images contained in COCO to X-rays is a much larger domain shift. While RF-DETR handled this jump best, even its performance shows the limits of fine-tuning, and there are times when you might need to consider more data, longer training, or even domain-specific pretraining.</p>



<p>The broader takeaway is that there is no single “best” detector: It is dependent on the task. Model size, latency requirements, licensing restrictions, and most importantly, the similarity between the model’s pretraining data and your target domain all affect the outcome. It is important to refrain from unquestioningly trusting the numbers reported by model providers and explore the fit of a specific model for your own particular task.</p>



<h2 class="wp-block-heading">Get started with PyCharm today</h2>



<p>In this post, we’ve gone from validating pretrained YOLO12, YOLO26, and RF-DETR checkpoints on COCO to testing them zero-shot on specialized data, to fine-tuning them on three very different object detection tasks, and then finally, comparing the resulting accuracy and latency. Along the way, we’ve seen how PyCharm can help manage the practical side of a project like this, where multiple model families require different dependency sets and training environments.</p>



<p>PyCharm helps you keep these workflows together in a single project while using isolated Python environments for each model family. Its interpreter management, built-in terminal, <em>Python Packages</em> tool window, and support for remote development make it easier to move between environments and run training on remote GPU hardware without having to manage each part of this workflow separately.</p>



<p>If you’d like to try these experiments yourself, maybe look into fine-tuning these models for your own specific object detection use case! PyCharm is available to download and try. You can use the accompanying project code to reproduce our COCO baselines, download the RF100 datasets, fine-tune the models, and evaluate them using the held-out test splits.</p>



<p>You can find the <a href="https://github.com/ArinaBelova/sota-object-detection-tutorial" target="_blank" rel="noopener">full code for this project on GitHub</a>. And if you’d like to learn more about object detection, including the architectures behind the models we used in this post, check out the <a href="https://blog.jetbrains.com/pycharm/2026/07/best-object-detection-models-for-machine-learning-in-2026/">previous post in this series</a>.</p>



<p></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>The State of Django 2026: Boring is so back</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/the-state-of-django-2026-boring-is-so-back/</link>
		
		<dc:creator><![CDATA[Will Vincent]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 14:17:28 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/Django_Preview_1280x720.png</featuredImage>		<category><![CDATA[pycharm]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[state-of-django]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=734624</guid>

					<description><![CDATA[Welcome to the highlights from the fifth annual Django Developers Survey, a collaboration between the Django Software Foundation and PyCharm. This year’s report draws on responses from nearly 3,500 Django developers across more than 40 countries — from students in their first year to veterans with decades of experience. In software, &#8220;boring&#8221; is a compliment. [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Welcome to the highlights from the fifth annual <a href="https://lp.jetbrains.com/django-developer-survey-2026/" target="_blank" rel="noopener">Django Developers Survey</a>, a collaboration between the <a href="https://www.djangoproject.com/foundation/" target="_blank" rel="noopener">Django Software Foundation</a> and <a href="https://www.jetbrains.com/pycharm/web-development/django/" target="_blank" rel="noopener">PyCharm</a>. This year’s report draws on responses from nearly 3,500 Django developers across more than 40 countries — from students in their first year to veterans with decades of experience.</p>



<p>In software, &#8220;boring&#8221; is a compliment. It means a technology works so reliably you can stop thinking about it. By that standard, Django in 2026 is thriving: PostgreSQL has been the database of choice for 76–79% of respondents for five consecutive years, Django&#8217;s template engine has held steady at around 80%, and nearly half of developers upgrade with every stable release — 43% are already on Django 6.0, months after it shipped. The core is boring in the best possible way. Mature, but not static.</p>



<p>Everything around that core, though, is moving fast. AI is now an everyday tool for most developers, and agents are beginning to move beyond answering questions to editing files, running commands, and completing larger tasks. Newer tools are consolidating workflows that once required several separate utilities, typing is becoming standard practice, and the boundaries between editors, terminals, automation, and AI are getting blurrier.</p>



<p>That may be Django’s particular advantage in 2026: it is mature enough to be dependable, active enough to keep moving, and stable enough to give developers room to change almost everything else.</p>



<p><em>As an open-source framework, Django depends on its community and needs funding to remain healthy and secure. The PyCharm team runs an annual fundraiser to support Django.</em></p>



<p><em>Until September 10, get PyCharm at 30% off, and JetBrains will donate 100% of your purchase amount to the Django Software Foundation.</em></p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://www.jetbrains.com/pycharm/promo/support-django/" class="btn" target="" rel="noopener">Get PyCharm at 30% off, and support Django</a>
                                                    </div>
    </div>







<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_infographics_2-3.png" alt="" class="wp-image-734782"/></figure>



<h2 class="wp-block-heading">1. AI is mainstream, but no workflow has won</h2>



<p>AI has become part of the normal Django development workflow. Only 10% of respondents said they regularly use no AI tools for coding, while 58% of AI users use them every day and another 27% several times a week.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_1.png" alt="" class="wp-image-734651"/></figure>



<p>What remains unsettled is how developers use them. The interfaces are split almost evenly across the browser, the IDE, and the command line, and no tool dominates: Claude Code leads at 35%, with ChatGPT just behind at 33% and GitHub Copilot at 23%. And for all the attention on agents, a majority of AI users — 56% — still use it purely for chat and advice.</p>



<h2 class="wp-block-heading">2. Dominating AI workflow is still supervised</h2>



<p>Nearly half of AI users already work with AI through an IDE integration. But despite the growth of coding agents, the dominant workflow is still supervised: 59% have AI generate code and then apply the changes themselves, 44% let it edit files or run commands when instructed, and only 27% use it to autonomously complete multi-step tasks.</p>



<p>The emerging pattern is therefore less “replace the IDE with an agent” and more “bring the agent into the development environment.” Developers are adopting AI quickly, but the editor remains their home base for understanding the codebase, reviewing changes, and deciding what makes it into the project.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_2.png" alt="" class="wp-image-734662"/></figure>



<p>Nowhere is the shift clearer than in how developers learn. Django&#8217;s official documentation remains the top resource at 67%, but AI tools are now second at 51% — ahead of YouTube, reading source code, and Stack Overflow.</p>



<p>AI is already routine for writing code, debugging, research, and learning. But developers are still experimenting with where it belongs: in the browser, inside the IDE, at the terminal, or increasingly, acting directly on the codebase.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>AI is changing the IDE faster than it is replacing it.</strong></p>
</blockquote>



<h2 class="wp-block-heading">3. Python tooling is consolidating fast</h2>



<p>Two tools that barely existed a few years ago are already near the top of the survey.</p>



<p>uv, released in February 2024, is already used by 43% of respondents for managing Python environments—second only to venv at 63% and ahead of Docker at 31%.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_3.png" alt="" class="wp-image-734673"/></figure>



<p>Ruff tells a similar story. At 43%, it is now the most widely used code-quality and formatting tool in the survey, ahead of IDE inspections at 27%, Black at 25%, pre-commit at 20%, and Flake8 at 17%.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_4.png" alt="" class="wp-image-734684"/></figure>



<p>The shift is not that Python’s older tools have disappeared. It is that newer tools increasingly cover jobs that once required several separate utilities. The result is a Python toolchain beginning to consolidate around fewer, faster, more capable tools.</p>



<h2 class="wp-block-heading">4. Type hints are winning. The type checker race is wide open.</h2>



<p>Type hints are becoming the norm in Django development: 57% already use them, and another 26% plan to. What is much less settled is how developers check those types.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_5-1.png" alt="" class="wp-image-734695"/></figure>



<p>Among developers who use type hints, the most popular option isn&#8217;t a standalone type checker at all: 40% rely on the checker built into their IDE. Mypy follows at 32%, Ruff at 29%, and Pyright/Pylance at 22%. Newer entrants are already appearing too, with Astral’s ty reaching 12% and Meta’s Pyrefly at 4%.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_6.png" alt="" class="wp-image-734706"/></figure>



<p>The practice, in other words, is converging faster than the tooling. Django developers increasingly agree that types are useful, but there is still no consensus on which tool should enforce them—or whether a separate tool is necessary at all.</p>



<p>That makes type checking an interesting space to watch in 2027: will one of the newer standalone tools break away from the pack, or will type checking increasingly become something developers simply expect their IDE to provide?</p>



<h2 class="wp-block-heading">5. As AI writes more code, verification matters more</h2>



<p>AI is moving beyond suggestions and into the codebase. That makes automated verification more important, not less.</p>



<p>The survey can&#8217;t tell us whether AI is driving greater adoption of tests or CI. What it does show is that most Django developers already have the infrastructure agents need: pytest is used by 45% of respondents and unittest by 43%, with pytest-django at 34%. </p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_7.png" alt="" class="wp-image-734757"/></figure>



<p>That testing culture sits alongside widespread CI/CD adoption — GitHub Actions is now used by a majority of respondents at 51%, with GitLab CI/CD at another 26%. Together, these create a natural feedback loop for agentic development: an agent can make a change, run the test suite, respond to failures, and hand the developer a result that has already passed the project&#8217;s checks.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_8.png" alt="" class="wp-image-734717"/></figure>



<p>Not everyone has that loop in place: 19% of respondents write no automated tests at all. As more code is delegated to agents, that fifth of developers is working without the safety net that makes delegation trustworthy.</p>



<p>The emerging agentic workflow may depend as much on verification as generation. The more code we delegate, the more valuable it becomes to have tests and pipelines that can quickly tell both developers and agents whether a change actually works.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Agents can generate code. Tests and pipelines tell them whether it works.</strong></p>
</blockquote>



<h2 class="wp-block-heading">6. One framework, two ways to build</h2>



<p>Two distinct ways of building with Django are now firmly mainstream: letting Django render the interface, or using it as the backend for a separate frontend. 72% of respondents use server-rendered templates, while 53% use Django for API-only applications and 46% use it as the backend for a single-page application or dedicated JavaScript frontend.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_9.png" alt="" class="wp-image-734730"/></figure>



<p>The balance is even clearer when developers are asked for their primary approach. Half primarily build server-rendered applications, while 44% primarily use Django for either APIs or dedicated JavaScript frontends.</p>



<p>The JavaScript numbers tell the sharper story. React has barely moved in five years — 37% in 2021, 38% today. What&#8217;s changed is everything around it: jQuery has fallen from 37% to 23%, Vue from 28% to 17%, while htmx has climbed from just 5% to 34%. htmx isn&#8217;t taking share from React — it&#8217;s modernizing the server-rendered side of the divide that jQuery used to own.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/Blog_graph_10.png" alt="" class="wp-image-734742"/></figure>



<p>That flexibility is one of Django’s strengths. The same framework can sit at the center of a hypermedia application or behind an API consumed by React, mobile apps, or other clients. </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Django remains unusually comfortable on both sides of the frontend divide.</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
</blockquote>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Across the survey, the pattern is consistent: developers are changing their tools and workflows far faster than they are changing the framework underneath them. The parts of Django they value most remain familiar — models, the admin, authentication — and even deployment stays defiantly unfashionable, with 54% shipping monoliths and 44% self-hosting.</p>



<p>Even where Django itself is evolving, it does so deliberately: 33% of respondents use its async features and another 40% plan to — change offered as an opt-in, not a rewrite.</p>



<p>That stability is increasingly valuable. Developers can experiment with a new agent, replace several tools with Ruff or uv, add htmx to a template, or adopt a new type checker without having to rethink the framework underneath their application.</p>



<p>Django&#8217;s advantage in 2026 is not that it is the newest thing. It is that it gives developers a solid, dependable — yes, boring — place from which to try the newest things. Boring is so back.</p>



<h2 class="wp-block-heading">PyCharm for Django Fundraiser</h2>



<p>Get a new PyCharm Pro license or renew your existing one at 30% off, with 100% of your purchase amount going to the Django Software Foundation.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://www.jetbrains.com/pycharm/promo/support-django/" class="btn" target="_blank" rel="noopener">Get PyCharm at 30% off, and support Django</a>
                                                    </div>
    </div>







<p>Explore the complete <a href="https://lp.jetbrains.com/django-developer-survey-2026/" target="_blank" rel="noopener">2026 Django Developers Survey Results</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Security Incident Affecting JetBrains Cadence</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/cadence-security-incident-august-2026/</link>
		
		<dc:creator><![CDATA[Daniel Gallo]]></dc:creator>
		<pubDate>Fri, 28 Aug 2026 09:50:14 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/JB-social-BlogFeatured-1280x720-1-9.png</featuredImage>		<category><![CDATA[news]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=734588</guid>

					<description><![CDATA[We are investigating a security incident affecting JetBrains Cadence. Cadence is a JetBrains-hosted service that integrates with PyCharm through an optional plugin, and lets you run your projects on cloud compute resources. Our investigation has confirmed unauthorized access to the service and the exposure of customer data associated with its use. We have contacted affected [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>We are investigating a security incident affecting JetBrains Cadence. Cadence is a JetBrains-hosted service that integrates with PyCharm through an optional plugin, and lets you run your projects on cloud compute resources. Our investigation has confirmed unauthorized access to the service and the exposure of customer data associated with its use.</p>



<p>We have contacted affected users directly and have taken steps to contain the incident.</p>



<p><strong>This post provides the latest information about the incident, its potential impact, and the actions we recommend Cadence users take. We will update it as our investigation progresses and additional information becomes available.</strong></p>



<p><strong>Last updated: </strong>August 31, 2026, 12:56 CEST</p>



<h2 class="wp-block-heading">August 31, 2026, 12:56 CEST</h2>



<p>Our investigation remains ongoing. At this time, we have not identified any additional compromised resources or data.</p>



<p>We have confirmed that the threat actor accessed data contained in the Cadence server backup from 2024. At this time, there is no evidence to suggest that the threat actor extracted data, including secrets, from the current Cadence environment.</p>



<p>The recommended actions described below remain unchanged.</p>



<h2 class="wp-block-heading">August 28, 2026, 11:50 CEST</h2>



<p><a href="https://blog.jetbrains.com/pycharm/2025/06/training-your-ml-models-with-cadence/">Cadence</a> is a JetBrains-hosted service integrated with PyCharm through an optional plugin, that lets you run your projects on cloud compute resources. Cadence uses JetBrains TeamCity to orchestrate this work. We recently disclosed <a href="https://blog.jetbrains.com/teamcity/2026/07/cve-2026-63077/">CVE-2026-63077</a>, a critical vulnerability in TeamCity that can allow an unauthenticated attacker to execute arbitrary commands on a vulnerable server.</p>



<p>We have since confirmed the Cadence environment was vulnerable to CVE-2026-63077 and was exploited through this vulnerability.</p>



<p>Cadence users should immediately revoke or rotate all credentials and secrets that may have been used to run their Cadence executions. They should also treat all executions, including their inputs and outputs in your Cadence project, as potentially untrusted.</p>



<h3 class="wp-block-heading"><strong>Actions required immediately</strong></h3>



<p>We strongly recommend that Cadence users:</p>



<ul class="wp-block-list">
<li>Revoke and rotate all credentials and secrets that may have been used to run Cadence executions.</li>



<li>Review connected systems for suspicious activity, particularly AWS accounts, S3 buckets, deployment environments, package/container registries, and other systems accessible using credentials mentioned above.</li>



<li>Review source code repositories for unauthorized changes made during the affected period.</li>



<li>Review any source code or project files synchronized to Cadence from PyCharm and rotate any credentials, tokens, or other sensitive information contained within them.</li>



<li>Treat all executions, including their inputs and outputs in your Cadence project, as potentially untrusted.</li>
</ul>



<p>Cadence users can contact us to request an inventory of the credentials and secrets associated with their Cadence usage. This may help users identify which credentials need to be revoked or rotated, but the inventory should not be considered exhaustive.</p>



<p>We have collated a list of Indicators of Compromise (IoCs) below. These indicators are not exhaustive, and the absence of these indicators does not confirm that an account or system was unaffected:</p>



<ul class="wp-block-list">
<li>Activity occurring from August 8, 2026, onwards, particularly authentication or activity using credentials previously stored in or accessible through Cadence.</li>



<li>IP addresses associated with observed exploitation activity:
<ul class="wp-block-list">
<li>150.109.230.104</li>



<li>43.153.227.206</li>



<li>62.210.127.48</li>



<li>210.247.242.190</li>



<li>15.235.225.205</li>



<li>152.233.30.18</li>
</ul>
</li>



<li>Authentication or other activity from unexpected IP addresses or locations.</li>



<li>Unexpected repository clones or downloads, and unexpected commits to repositories.</li>



<li>Changes to repository secrets, webhooks, collaborators, or permissions.</li>



<li>New or modified personal access tokens, API tokens, or SSH keys in external services.</li>



<li>New service accounts created in external services.</li>



<li>Unexpected changes to cloud IAM roles, policies, or permissions.</li>



<li>Unexpected access to cloud storage, including S3 buckets and objects, in services such as AWS and Google Cloud.</li>



<li>Unexpected publication or modification of packages or releases.</li>
</ul>



<h3 class="wp-block-heading"><strong>Affected server</strong></h3>



<p>We have confirmed that the following Cadence server was successfully exploited: <code>api.cadence.jetbrains.com</code>.</p>



<h3 class="wp-block-heading"><strong>Affected period</strong></h3>



<p>August 8, 2026, to August 24, 2026.</p>



<h3 class="wp-block-heading"><strong>What happened</strong></h3>



<p>The Cadence server used TeamCity to orchestrate workloads and was vulnerable to CVE-2026-63077. Threat actors exploited the vulnerability and gained unauthorized access to the affected Cadence environments, with activity identified from August 8, 2026. We discovered the exploitation on August 23, 2026, and took the affected server offline on August 24, 2026, while we continued our investigation.</p>



<h3 class="wp-block-heading"><strong>What we know</strong></h3>



<p>Our investigation is ongoing, but we have confirmed that the threat actors:</p>



<ul class="wp-block-list">
<li>Accessed personal data and extracted it from the affected environment. Confirmed affected personal data includes usernames, real names, email addresses, last-login timestamps, and last accessed IP addresses.</li>



<li>Compromised a full backup of the Cadence server dating from 2024. This means credentials, configuration, artifacts, logs, or other data present in that backup must also be treated as potentially exposed.</li>



<li>Compromised multiple AWS IAM users and associated credentials/secrets used with Cadence, including IAM users belonging to JetBrains employees who used the service. These credentials were present in the compromised 2024 backup.</li>



<li>Accessed files stored in S3 buckets within JetBrains AWS accounts used by Cadence. We are still determining the full scope of the data accessed. We do not currently know whether the threat actors accessed storage buckets in customer accounts. However, some users may have configured Cadence to access their own storage buckets, and the credentials used for those connections may have also been exposed.</li>



<li>May have accessed source code synchronized from PyCharm projects to the affected server. If you used PyCharm to upload or synchronize project files for execution in Cadence, you should treat that code, and any credentials or configuration contained within it, as potentially compromised.</li>
</ul>



<p>The likely consequences of the personal data exposure include an increased risk of targeted phishing, social engineering, impersonation, and other unsolicited or malicious communications using the affected names and email addresses.</p>



<p>As the threat actors gained access to the Cadence server, any credentials or secrets stored in Cadence, contained in the compromised backup, or made available to executions on the affected server should be considered compromised and must be revoked or rotated.</p>



<p>This includes but is not limited to:</p>



<ul class="wp-block-list">
<li>Cloud credentials, including AWS, Azure, and Google Cloud.</li>



<li>Source control credentials and tokens, including GitHub, GitLab, and Bitbucket.</li>



<li>Package repository credentials, including npm, Maven, NuGet, PyPI, and similar services.</li>



<li>Container registry credentials, including Docker Hub, ECR, GCR, ACR, and other registries.</li>



<li>Slack tokens, webhooks, API tokens, SSH/deployment keys, service account credentials, signing keys/certificates, and credentials for any other external systems used by your Cadence executions.</li>
</ul>



<h3 class="wp-block-heading"><strong>Actions JetBrains has taken</strong></h3>



<p>We took the Cadence server offline on August 24, 2026, while we continue to investigate the incident. At present, we have confirmed that the incident is limited to data associated with the Cadence host mentioned above.</p>



<p>The server should have been patched as part of our response to the vulnerability, but it was not. We sincerely apologize for this failure and the impact it may have on you.</p>



<p>We have invalidated all access tokens used by the JetBrains Cadence plugin in PyCharm to connect to Cadence, and took the server offline on August 24, 2026.</p>



<p>We are also notifying the relevant authorities and taking the necessary steps to protect the data of Cadence users.</p>



<h3 class="wp-block-heading"><strong>Further updates</strong></h3>



<p>We will publish further findings and guidance here as our investigation progresses. We recommend checking this page frequently for the latest information. We will also contact affected users directly if we identify any important new information that may require action on their part.</p>



<p>For more information about the underlying vulnerability, please see our original <a href="https://blog.jetbrains.com/teamcity/2026/07/cve-2026-63077/">security advisory</a> to TeamCity customers and users.</p>



<p>If you previously used Cadence and need assistance identifying which credentials may have been exposed or have any questions regarding this incident, contact the JetBrains Security team at <a href="mailto:security@jetbrains.com?subject=Security incident affecting JetBrains Cadence">security@jetbrains.com</a>.</p>



<p>We recognize the seriousness of this incident and apologize again for the impact.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>OpenTelemetry Comes to IntelliJ IDEA, GoLand, PyCharm, and WebStorm</title>
		<link>https://blog.jetbrains.com/platform/2026/08/opentelemetry-plugin-for-jetbrains-ides/</link>
		
		<dc:creator><![CDATA[Egor Klimov]]></dc:creator>
		<pubDate>Wed, 26 Aug 2026 07:40:10 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/JB-social-BlogFeatured-1280x720-1-6.png</featuredImage>		<product ><![CDATA[go]]></product>
		<product ><![CDATA[pycharm]]></product>
		<product ><![CDATA[webstorm]]></product>
		<category><![CDATA[goland]]></category>
		<category><![CDATA[idea]]></category>
		<category><![CDATA[intellij-platform]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[pycharm]]></category>
		<category><![CDATA[research]]></category>
		<category><![CDATA[rider]]></category>
		<category><![CDATA[webstorm]]></category>
		<category><![CDATA[intellij-idea]]></category>
		<category><![CDATA[opentelemetry]]></category>
		<category><![CDATA[performance-optimization]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=platform&#038;p=733710</guid>

					<description><![CDATA[OpenTelemetry Comes to IntelliJ IDEA, GoLand, PyCharm, and WebStorm. The OpenTelemetry plugin has broken out of the confines of JetBrains Rider. No sandbox exploit was involved – this escape was planned by our developers. With the 2026.2 release, the OpenTelemetry plugin is now available in IntelliJ IDEA, GoLand, PyCharm and WebStorm. Rider users needn’t worry [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>OpenTelemetry Comes to IntelliJ IDEA, GoLand, PyCharm, and WebStorm.</p>



<p>The <a href="https://plugins.jetbrains.com/plugin/27488-opentelemetry/" target="_blank" rel="noopener">OpenTelemetry plugin</a> has broken out of the confines of JetBrains Rider. No sandbox exploit was involved – this escape was planned by our developers. With the 2026.2 release, the OpenTelemetry plugin is now available in IntelliJ IDEA, GoLand, PyCharm and WebStorm. Rider users needn’t worry – it still works there too.</p>



<p>The plugin brings logs, metrics, traces, and the service map from local applications into the IDE. You can inspect them without setting up a separate local observability backend.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://plugins.jetbrains.com/plugin/27488-opentelemetry/" class="btn" target="_blank" rel="noopener">Get the OTel plugin from the Marketplace</a>
                                                    </div>
    </div>







<h2 class="wp-block-heading">What is the OpenTelemetry plugin?</h2>



<p>While developing with your IDE, you can use the plugin to:</p>



<ul class="wp-block-list">
<li>Find what happened immediately before an error.</li>



<li>See which services and dependencies a request reached.</li>



<li>Pinpoint where a request spent its time.</li>



<li>Check whether the application emitted the expected logs, metrics, and traces.</li>



<li>Ask your coding agent to do all of the above via <a href="https://www.jetbrains.com/help/idea/mcp-server.html" target="_blank" rel="noopener">MCP</a>.</li>
</ul>



<p></p>



<p>The plugin’s functionality complements what the IDE offers out of the box. Use a debugger to step through code, a profiler to analyze performance, and a production monitoring platform to watch deployed systems. Use the OpenTelemetry plugin to inspect runtime behavior while you build or test the application.</p>



<h2 class="wp-block-heading">Imagine this</h2>



<p>Say you are testing a feature that calls several services and a database before publishing a message to a queue. The request fails, and the console shows the exception, but not the path that led to it.</p>



<p>In the <em>OpenTelemetry</em> tool window, you can search the logs for the error. Open the relevant trace to see how the request moved through the system and where it failed. The <em>Service Map</em> shows the services and infrastructure involved.</p>



<h2 class="wp-block-heading">Explore runtime data in the IDE</h2>



<h3 class="wp-block-heading">Search and inspect logs</h3>



<p>Console output is manageable until several services start writing at once. The <em>Logs</em> view puts OpenTelemetry log records in a searchable table. You can filter by severity or content, then open a record to inspect its attributes.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image3-3.png" alt="" class="wp-image-733792"/><figcaption class="wp-element-caption"><em>Detailed log view showing timestamps, levels, and messages for different log types</em></figcaption></figure>



<h3 class="wp-block-heading">Check metrics</h3>



<p>Select a metric from the metric tree to plot its values while you use the application. This view is not a replacement for your production dashboards, but it lets you inspect what the application will export before you send the data to a production observability platform. If a new library adds noisy or unnecessary metrics, you can catch them locally and adjust the instrumentation before your DevOps and SRE colleagues have to handle them.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image2.png" alt="" class="wp-image-733803"/><figcaption class="wp-element-caption"><em>The metrics viewer displays CPU utilization data with real-time charts.</em></figcaption></figure>



<h3 class="wp-block-heading">Follow a request through its trace</h3>



<p>Open a trace to see its spans across services. Each span includes timing and attributes, so you can follow the request from start to finish and zero in on the operation that failed or slowed things down. Development is also a good time to look at the trace itself. Does it contain the spans and details you&#8217;d need during a real incident? It&#8217;s much easier to fix those gaps now than to discover them in production.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image4-2.png" alt="" class="wp-image-733781"/><figcaption class="wp-element-caption"><em><em>The trace table view with filtering capabilities and detailed information for the selected trace.</em></em></figcaption></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image1-1.png" alt="" class="wp-image-733814"/><figcaption class="wp-element-caption"><em>The trace viewer showing details of the POST request involving multiple internal DB and HTTP calls.</em></figcaption></figure>



<p></p>



<h3 class="wp-block-heading">See observed relationships in the <em>Service Map</em></h3>



<p>Architecture diagrams go stale. The <em>Service Map</em> builds its relationships from collected traces, so it reflects the traffic the plugin has seen between services, endpoints, databases, and message queues.</p>



<p>Expected one HTTP call or database query, but the diagram shows several? Finding that during development gives you time to fix it before release.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/image5-2.png" alt="" class="wp-image-733825"/><figcaption class="wp-element-caption"><em>Automatically generated service map showing the relationship between API endpoints, internal services, and PostgreSQL based on actual runtime traces.</em></figcaption></figure>



<h2 class="wp-block-heading">Connect an instrumented application</h2>



<p>The plugin does not add OpenTelemetry libraries or agents to your application. It configures where an already instrumented application sends its data.</p>



<p>Start an instrumented Java, Python, Go, or .NET application from a supported IDE run configuration, and the plugin passes OpenTelemetry Protocol (OTLP) environment variables that point to its built-in receiver. It passes the same variables to new integrated terminal sessions.</p>



<p>You can also point the application&#8217;s OTLP exporter at the endpoint shown by the plugin. If you already use a local OpenTelemetry Collector, add the plugin as an OTLP destination and keep the rest of your pipeline.</p>



<h2 class="wp-block-heading">Runtime context for coding agents</h2>



<p>The plugin has experimental MCP support through the <a href="https://plugins.jetbrains.com/plugin/26071-mcp-server" target="_blank" rel="noopener">JetBrains MCP server</a>. A compatible coding agent can query the telemetry collected in the IDE with these tools:</p>



<ul class="wp-block-list">
<li><code>get_log_records</code></li>



<li><code>get_spans</code></li>



<li><code>get_services</code></li>



<li><code>get_service_map</code></li>
</ul>



<p>These tools give the agent evidence from a particular run, so that it can inspect the logs and spans or query the observed service relationships.</p>



<h2 class="wp-block-heading">Getting started</h2>



<ol class="wp-block-list">
<li>Install the <a href="https://plugins.jetbrains.com/plugin/27488-opentelemetry/" target="_blank" rel="noopener">OpenTelemetry plugin from JetBrains Marketplace</a> for your favorite IDE.</li>



<li>Instrument your application with OpenTelemetry libraries or agents.</li>



<li>Start it from a supported IDE run configuration or a new integrated terminal session. The plugin will pass the OTLP environment variables. Alternatively, point the application&#8217;s exporter or your local collector at the endpoint shown by the plugin.</li>



<li>Open the <em>OpenTelemetry</em> tool window and exercise the part of the application you want to inspect.</li>
</ol>



<p>The plugin shows the signals it receives. If your application exports only traces, the <em>Logs</em> and <em>Metrics</em> views stay empty.</p>



<p>Full setup instructions are available in the <a href="https://www.jetbrains.com/help/rider/OpenTelemetry.html" target="_blank" rel="noopener">OpenTelemetry plugin documentation</a>.</p>



<h2 class="wp-block-heading">Install it and tell us what you find</h2>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://plugins.jetbrains.com/plugin/27488-opentelemetry/" class="btn" target="_blank" rel="noopener">Get the OTel plugin from the Marketplace</a>
                                                    </div>
    </div>







<p>Try it on a real project, then tell us in the comments or through the <a href="https://youtrack.jetbrains.com/projects/RIDER/issues/RIDER-102188/OpenTelemetry-plugin-for-Rider" target="_blank" rel="noopener">issue tracker</a> what worked and what still sent you to another tool.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PyCharm for AI-assisted Django Workflows</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/pycharm-for-ai-assisted-django-workflows/</link>
		
		<dc:creator><![CDATA[Will Vincent]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 13:06:50 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-social-BlogFeatured-1280x720-1-1.png</featuredImage>		<category><![CDATA[agentic-ai]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web-development]]></category>
		<category><![CDATA[django]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=731652</guid>

					<description><![CDATA[The 2026 Django Developers Survey found that AI is part of the weekly or daily workflow for 90% of respondents. AI can write code quickly, but Django developers still need to understand the application, evaluate what the agent produces, and be accountable for what ships. That makes your IDE more important, not less. PyCharm gives [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>The <em><a href="https://lp.jetbrains.com/django-developer-survey-2026/" target="_blank" rel="noopener">2026 Django Developers Survey</a></em> found that AI is part of the weekly or daily workflow for 90% of respondents. AI can write code quickly, but Django developers still need to understand the application, evaluate what the agent produces, and be accountable for what ships.</p>



<p>That makes your IDE more important, not less. PyCharm gives you the freedom to choose the agents and models you want, as well as extensive Python and Django support for understanding and reviewing the code they produce.</p>



<h3 class="wp-block-heading">1. Bring your own agent</h3>



<p>Leading agents such as Codex, Claude Agent, Junie, and Gemini run natively in PyCharm, while the <a href="https://agentclientprotocol.com/get-started/registry" target="_blank" rel="noopener">ACP registry</a> gives you access to dozens more, installable in a click from the same dropdown menu. You can use a JetBrains AI subscription or bring your own tools – the choice is yours.</p>



<p>Want to use your own model? <a href="https://www.jetbrains.com/help/ai-assistant/bring-your-own-key-byok.html" target="_blank" rel="noopener">Bring Your Own Key</a> technology lets you connect existing provider credentials, while Ollama and LM Studio let you work with local models.</p>



<p>You choose the AI tools that fit your workflow. PyCharm doesn&#8217;t lock you into one provider.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/1_AIChat_Dropdown.webm"></video></figure>



<h3 class="wp-block-heading">2. Teach your agent your conventions</h3>



<p>Skills give your agent reusable instructions and context. In PyCharm 2026.2, native skill support for Claude Agent and Codex lets you add a skill directly from the AI chat, either for a single project or across your entire codebase.</p>



<p>The curated <a href="https://blog.jetbrains.com/ai/2026/04/skill-manager-and-skill-repository/">Skill Repository</a> also gives you a way to add official skills for technologies, including React, Postgres, and Playwright.</p>



<p>Skills let you encode useful context once and reuse it, so you spend less time repeating instructions to your agents.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/2_Agent_Skills.webm"></video></figure>



<h3 class="wp-block-heading">3. Django 6+ support</h3>



<p>PyCharm keeps up with Django&#8217;s release cycle, including<a href="https://www.jetbrains.com/help/pycharm/django-6-template-partials.html" target="_blank" rel="noopener"> Django 6.0 template partials</a>. The IDE understands the new partial template tags and completes them as you type.</p>



<p>This matters especially when frameworks evolve. An agent&#8217;s training data may not reflect the exact Django version you&#8217;re running. PyCharm&#8217;s Django support is tied to the version in your project.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/3_Django60_Template_Partials.webm"></video></figure>



<h3 class="wp-block-heading">4. Review with confidence</h3>



<p>AI generates code quickly, which makes reviewing and undoing changes more important than ever.</p>



<p>PyCharm gives you visual diffs, merge tools, integrated conflict resolution, and Git history in the IDE, so you can inspect changes before committing or merging.</p>



<p><a href="https://www.jetbrains.com/help/pycharm/local-history.html" target="_blank" rel="noopener">Local History</a> works independently of version control, recording changes as you work. You can compare a file with an earlier state and restore a version from before the agent touched it – even if you never committed the change.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/4_Review_Confidence.webm"></video></figure>



<h3 class="wp-block-heading">5. See your Django architecture</h3>



<p>Django applications can get big<strong>. </strong><a href="https://www.jetbrains.com/help/pycharm/django-logical-structure.html" target="_blank" rel="noopener">Django Logical Structure</a> presents your project from Django&#8217;s point of view rather than as a flat collection of Python and HTML files. You can follow a model to its serializer, its views, and the endpoints they serve.</p>



<p>Whether the code you&#8217;re reading was written by you six months ago, another developer, or an agent, PyCharm helps you understand how the pieces fit together.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/5_Django_Structure.webm"></video></figure>



<h3 class="wp-block-heading">6. Verify your API and your data</h3>



<p>The<a href="https://www.jetbrains.com/help/pycharm/endpoints-tool-window.html" target="_blank" rel="noopener"><em> </em><em>Endpoints </em>tool window</a> gives you a structured view of your Django endpoints, including documentation, examples, generated HTTP requests, and OpenAPI information. You can send those requests directly with the built-in HTTP Client.</p>



<p>The same idea applies to your database. The<a href="https://www.jetbrains.com/help/pycharm/data-editor-and-viewer.html" target="_blank" rel="noopener"> data editor and viewer</a> let you browse and query application data without leaving the IDE. After an agent generates a migration, you can inspect the resulting data and verify that the change did what you expected.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/6_Export_API_DB.webm"></video></figure>



<h3 class="wp-block-heading">The IDE outlasts the agent</h3>



<p>AI tooling is changing fast, but your developer workflow can stay consistent. PyCharm combines deep Python and Django support with access to the agents and models you choose.</p>



<p>Get 30% off PyCharm, and JetBrains will donate 100% of what you pay to the Django Software Foundation.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://www.jetbrains.com/pycharm/promo/support-django/" class="btn" target="" rel="noopener">Get 30% off PyCharm</a>
                                                    </div>
    </div>




]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>What’s Fixed and Improved in PyCharm 2026.2</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/what-s-fixed-and-improved-in-pycharm-2026-2/</link>
		
		<dc:creator><![CDATA[Will Vincent]]></dc:creator>
		<pubDate>Wed, 19 Aug 2026 13:54:13 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/Whats-fixed-2026-2-BlogFeatured-1280x720-1.png</featuredImage>		<category><![CDATA[releases]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[web-development]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=731629</guid>

					<description><![CDATA[Across the PyCharm 2026.2 release line, we shipped 263 fixes and improvements. Many improve Python code insight directly, with more precise type inference, fewer false positives, smarter completion and imports, and more reliable refactoring. Here are some of the smaller changes you’re likely to notice in everyday Python development. SQLAlchemy 2.0 support SQLAlchemy has been [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Across the PyCharm 2026.2 release line, we shipped<a href="https://youtrack.jetbrains.com/issues?q=%23py%20type:%20bug%20%23resolved%20Planned%20for:%202026.2,%20%7B2026.2%20*%7D,%202026.2.1%20visible%20to:%20%7BAll%20Users%7D" target="_blank" rel="noopener"> 263 fixes and improvements</a>. Many improve Python code insight directly, with more precise type inference, fewer false positives, smarter completion and imports, and more reliable refactoring. Here are some of the smaller changes you’re likely to notice in everyday Python development.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">SQLAlchemy 2.0 support</h2>



<p>SQLAlchemy has been a long-standing source of false positives – enough that several duplicate tickets have accumulated over the years. This release resolves a batch of them for the 2.0 style.</p>



<p>String forward-references inside <code>Mapped[...]</code> resolve correctly:</p>



<pre class="EnlighterJSRAW">posts: Mapped&#091;list&#091;&quot;Post&quot;]] = relationship(back_populates=&quot;author&quot;)

# &quot;Post&quot; now resolves to the model class</pre>



<p>PyCharm also correctly infers the mapped type returned by <code>Session.get()</code>, instead of treating the result as the model class itself:</p>



<pre class="EnlighterJSRAW">report = session.get(Report, report_id)

reveal_type(report)&nbsp; # was: type&#091;Report] | None &nbsp; now: Report | None</pre>



<p>Modern <code>hybrid_property</code> setters written as <code>@name.inplace.setter</code> are recognized, so assigning to the property no longer produces a warning. Model class attributes defined via mixins are picked up again, too, clearing the old <em>unexpected argument</em> reports on model constructors.</p>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-78816" target="_blank" rel="noopener">PY-78816</a>,<a href="https://youtrack.jetbrains.com/issue/PY-65142" target="_blank" rel="noopener"> PY-65142</a>,<a href="https://youtrack.jetbrains.com/issue/PY-59732" target="_blank" rel="noopener"> PY-59732</a>,<a href="https://youtrack.jetbrains.com/issue/PY-51906" target="_blank" rel="noopener"> PY-51906</a>,<a href="https://youtrack.jetbrains.com/issue/PY-28762" target="_blank" rel="noopener"> PY-28762</a>)</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Code insight and type inference</h2>



<h3 class="wp-block-heading">Control-flow narrowing and &#8220;unreachable code&#8221;</h3>



<p>Several false <em>This code is unreachable</em> reports and instances of lost narrowing across loops have been fixed. The common issue: flow analysis either gave up or over-eagerly narrowed to <code>Never </code>in branches it should have kept alive.</p>



<p><code>isinstance</code> on a numeric union no longer kills the <code>else</code> branch:</p>



<pre class="EnlighterJSRAW">def foo(y: int | float) -&gt; None:

&nbsp;&nbsp;&nbsp;&nbsp;if isinstance(y, float):

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pass

&nbsp;&nbsp;&nbsp;&nbsp;else:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print(y)&nbsp; # was flagged unreachable, y inferred as Never</pre>



<p>Narrowing also survives a <code>while</code> loop, so re-narrowing an optional attribute inside the loop body no longer reports a bogus <em>has no attribute</em> error.</p>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-83206" target="_blank" rel="noopener">PY-83206</a>,<a href="https://youtrack.jetbrains.com/issue/PY-83354" target="_blank" rel="noopener"> PY-83354</a>,<a href="https://youtrack.jetbrains.com/issue/PY-88265" target="_blank" rel="noopener"> PY-88265</a>)</p>



<h3 class="wp-block-heading">Strings inside type annotations</h3>



<p>A string used as metadata inside <code>Annotated[...]</code> – a Pydantic discriminator field name, for instance – is no longer parsed as a forward reference and flagged as unresolved.</p>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-48749" target="_blank" rel="noopener">PY-48749</a>,<a href="https://youtrack.jetbrains.com/issue/PY-82245" target="_blank" rel="noopener"> PY-82245</a>)</p>



<h3 class="wp-block-heading">Iterable unpacking and star expressions</h3>



<p>PyCharm&#8217;s analysis of tuple and star unpacking could lose type information and fall back to <code>Any</code>. Unpacking a starred value into a tuple lost its element types, <code>*</code>-expansion collapsed to <code>Any</code>, and several genuine errors went unreported. Starred expressions preserve their element types:</p>



<pre class="EnlighterJSRAW">def a() -&gt; tuple&#091;int, int]:

&nbsp;&nbsp;&nbsp;&nbsp;return 2, 3

def b() -&gt; tuple&#091;int, int, int]:

&nbsp;&nbsp;&nbsp;&nbsp;return (1, *a())&nbsp; # no more bogus &quot;Expected tuple&#091;int, int, int]&quot;</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-12592" target="_blank" rel="noopener">PY-12592</a>,<a href="https://youtrack.jetbrains.com/issue/PY-27205" target="_blank" rel="noopener"> PY-27205</a>,<a href="https://youtrack.jetbrains.com/issue/PY-43585" target="_blank" rel="noopener"> PY-43585</a>,<a href="https://youtrack.jetbrains.com/issue/PY-90219" target="_blank" rel="noopener"> PY-90219</a>)</p>



<h3 class="wp-block-heading">Augmented assignment</h3>



<p>A cluster of false positives came from augmented assignments being misanalyzed. A simple <code>/=</code> on an <code>int</code> produced the wrong type:</p>



<pre class="EnlighterJSRAW">foo = 5

foo /= 2

reveal_type(foo)&nbsp; # was: int &nbsp; now: float | int</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-80622" target="_blank" rel="noopener">PY-80622</a>)</p>



<h3 class="wp-block-heading"><code>Self</code> and constructor return types</h3>



<p><code>Self</code> binds correctly through <code>classmethod</code> parameters typed as <code>type[Self]</code>:</p>



<pre class="EnlighterJSRAW">class A:

&nbsp;&nbsp;&nbsp;&nbsp;@classmethod

&nbsp;&nbsp;&nbsp;&nbsp;def bar(cls, y: type&#091;Self]) -&gt; Self: ...

x = A.bar(A)&nbsp; &nbsp; &nbsp; # was a spurious &quot;Expected type&#091;A], got type&#091;A]&quot;

reveal_type(x)&nbsp; &nbsp; # was: Any &nbsp; now: A</pre>



<p>Construction also respects <code>__new__</code>, <code>__init__</code>, and metaclass <code>__call__</code>. When <code>__new__</code> returns something other than an instance, that&#8217;s the constructed type – even when an <code>__init__</code> is present. The same fix covers explicitly parameterized calls like <code>MyClass[int]()</code> and <code>__new__</code> assigned as a class attribute.</p>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-89296" target="_blank" rel="noopener">PY-89296</a>,<a href="https://youtrack.jetbrains.com/issue/PY-77611" target="_blank" rel="noopener"> PY-77611</a>,<a href="https://youtrack.jetbrains.com/issue/PY-88644" target="_blank" rel="noopener"> PY-88644</a>,<a href="https://youtrack.jetbrains.com/issue/PY-89571" target="_blank" rel="noopener"> PY-89571</a>)</p>



<h3 class="wp-block-heading">Enum members: Literal types for <code>.value</code> and <code>.name</code></h3>



<p>Reading an enum member&#8217;s <code>.value</code> or <code>.name</code> yields a precise <code>Literal</code> instead of a widened <code>str</code> or <code>int</code>, so assignments to <code>Literal[...]</code> target type-check. This matches <code>mypy</code>’s inference:</p>



<pre class="EnlighterJSRAW">from enum import Enum

from typing import Literal

class E(Enum):

&nbsp;&nbsp;&nbsp;&nbsp;a = &quot;a&quot;

b: Literal&#091;&quot;a&quot;] = E.a.value &nbsp; # was: Expected &#039;Literal&#091;&quot;a&quot;]&#039;, got &#039;str&#039;

n: Literal&#091;&quot;a&quot;] = E.a.name&nbsp; &nbsp; # .name is a Literal too</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-61028" target="_blank" rel="noopener">PY-61028</a>,<a href="https://youtrack.jetbrains.com/issue/PY-79198" target="_blank" rel="noopener"> PY-79198</a>)</p>



<h3 class="wp-block-heading">Parameter types inferred from decorators</h3>



<p>When a decorator constrains the callable it accepts, the decorated function&#8217;s parameters are inferred from that constraint instead of falling back to <code>Any</code>:</p>



<pre class="EnlighterJSRAW">from typing import Callable

def d(fn: Callable&#091;&#091;int], str]): ...

@d

def f(a):

&nbsp;&nbsp;&nbsp;&nbsp;reveal_type(a) &nbsp; # was: Any &nbsp; now: int</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-79204" target="_blank" rel="noopener">PY-79204</a>)</p>



<h3 class="wp-block-heading">Also fixed</h3>



<ul class="wp-block-list">
<li>Keyword arguments in a class header are validated against the base class&#8217;s <code>__init_subclass__</code> signature, and offered in completion (<a href="https://youtrack.jetbrains.com/issue/PY-79173" target="_blank" rel="noopener">PY-79173</a>).</li>



<li>An ellipsis in a <code>Callable</code> used as a PEP 695 type-parameter bound no longer reports a bogus <em>Invalid type expression</em> (<a href="https://youtrack.jetbrains.com/issue/PY-83570" target="_blank" rel="noopener">PY-83570</a>).</li>



<li>Type-checker findings are split into granular suppression codes rather than a single <code>PyTypeChecker</code> id, and <code># noinspection</code> directives accept a simplified name form. <code>PyTypeChecker</code> still works as a blanket ignore (<a href="https://youtrack.jetbrains.com/issue/PY-90265" target="_blank" rel="noopener">PY-90265</a>).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Completion and auto-import</h2>



<h3 class="wp-block-heading">Smarter auto-import&nbsp;</h3>



<p>Auto-import is now noticeably less noisy. Previously, if a module was already imported, PyCharm would offer to add a second, redundant import instead of qualifying through the one you already had. The quick-fix – and the completion popup – prefer to reuse the existing import.</p>



<p>Given <code>pkg/src.py</code> containing <code>MyClass</code>, and a file that already imports the module, <em>Alt</em>+<em>Enter</em> produces this:</p>



<pre class="EnlighterJSRAW">from pkg import src&nbsp; # no longer flagged as unused

src.MyClass</pre>



<p>instead of adding <code>from pkg.src import MyClass</code>. The same reuse logic applies to plain <code>import pkg.src</code>, and to the auto-import completion on a second <em>Ctrl</em>+<em>Space</em>.</p>



<p>Nested classes can be auto-imported too, which is something PyCharm didn&#8217;t previously support:</p>



<pre class="EnlighterJSRAW"># mod.py

class Outer:

&nbsp;&nbsp;&nbsp;&nbsp;class Inner:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pass

# main.py – Alt+Enter on Inner now offers &quot;Import Outer from mod&quot;

from mod import Outer

value = Outer.Inner()</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-87970" target="_blank" rel="noopener">PY-87970</a>,<a href="https://youtrack.jetbrains.com/issue/PY-87971" target="_blank" rel="noopener"> PY-87971</a>,<a href="https://youtrack.jetbrains.com/issue/PY-87972" target="_blank" rel="noopener"> PY-87972</a>,<a href="https://youtrack.jetbrains.com/issue/PY-88009" target="_blank" rel="noopener"> PY-88009</a>,<a href="https://youtrack.jetbrains.com/issue/PY-88016" target="_blank" rel="noopener"> PY-88016</a>)</p>



<h3 class="wp-block-heading">Completion for <code>unittest.mock.patch()</code> targets</h3>



<p>Patching by string target previously offered no code assistance, so dotted paths had to be entered manually. The string argument to <code>mock.patch(...)</code> gets code completion for modules, classes, and their attributes, and it no longer suggests the invalid <code>as</code> keyword mid-path:</p>



<pre class="EnlighterJSRAW">from unittest import mock

# sample.py defines: class Foo: my_attr = 42

with mock.patch(&quot;sample.Foo.my_attr&quot;, 14):

&nbsp;&nbsp;&nbsp;&nbsp;...

# completion now offers `sample`, `Foo`, and `my_attr`</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-89189" target="_blank" rel="noopener">PY-89189</a>,<a href="https://youtrack.jetbrains.com/issue/PY-89191" target="_blank" rel="noopener"> PY-89191</a>,<a href="https://youtrack.jetbrains.com/issue/PY-89192" target="_blank" rel="noopener"> PY-89192</a>)</p>



<h3 class="wp-block-heading">Typed signatures when overriding built-in methods</h3>



<p>Completing an override of a dunder or built-in method fills in the full annotated signature – and auto-imports the types it needs – instead of bare parameters:</p>



<pre class="EnlighterJSRAW">from types import TracebackType

class A:

&nbsp;&nbsp;&nbsp;&nbsp;def __exit__(self, exc_type: type&#091;BaseException] | None,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exc_val: BaseException | None,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;exc_tb: TracebackType | None): ...

# was: def __exit__(self, exc_type, exc_val, exc_tb):</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-79218" target="_blank" rel="noopener">PY-79218</a>)</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Editor and inspections</h2>



<h3 class="wp-block-heading">Type inlay hints</h3>



<p>Inferred type arguments are shown inline at the call site, so you can see what a generic resolved to without hovering over it:</p>



<pre class="EnlighterJSRAW">class A&#091;T]:

&nbsp;&nbsp;&nbsp;&nbsp;def __init__(self, t: T): ...

A&#091;int](1) &nbsp; &nbsp; # &#091;int] shown as an inlay hint</pre>



<p>Type names rendered inside inlay hints – return types and solved arguments alike – are also clickable, so you can jump straight to a type&#8217;s definition from the hint.</p>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-90411" target="_blank" rel="noopener">PY-90411</a>,<a href="https://youtrack.jetbrains.com/issue/PY-90293" target="_blank" rel="noopener"> PY-90293</a>)</p>



<h3 class="wp-block-heading"><code>f-string</code> format-spec validation</h3>



<p>PyCharm already validated the <code>str.format()</code> mini-language. Those checks apply to <code>f-strings</code> too, and PyCharm flags formatting a type that doesn&#8217;t implement <code>__format__</code>:</p>



<pre class="EnlighterJSRAW">data = 1

f&quot;{data:.2f}&quot; &nbsp; # ok

f&quot;{data:.2q}&quot; &nbsp; # now flagged: unsupported format spec

class A: ...

f&quot;{A():d}&quot;&nbsp; &nbsp; &nbsp; # now flagged: A doesn&#039;t support the &#039;d&#039; format</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-51322" target="_blank" rel="noopener">PY-51322</a>,<a href="https://youtrack.jetbrains.com/issue/PY-89760" target="_blank" rel="noopener"> PY-89760</a>)</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Refactoring</h2>



<p>The <em>Rename</em> refactoring also updates references to a module when the module itself is renamed. Previously, the renaming left importing sites pointing at the old name:</p>



<pre class="EnlighterJSRAW"># rename provider/provider_module.py → some_module.py

from ..provider import provider_module&nbsp; # this reference is updated too</pre>



<p>(<a href="https://youtrack.jetbrains.com/issue/PY-53274" target="_blank" rel="noopener">PY-53274</a>)</p>



<p>The <em>Refactor | Field action</em> is now <em>Attribute</em>, and the documentation says &#8220;instance attributes&#8221; to match Python terminology (<a href="https://youtrack.jetbrains.com/issue/PY-85828" target="_blank" rel="noopener">PY-85828</a>).</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Taken together, these changes make PyCharm’s understanding of Python more precise and predictable: fewer false positives, better type inference, smarter completion, and less time spent working around cases where the IDE gets valid code wrong.</p>



<p>Many of these improvements started with real-world examples reported by users. If PyCharm still misunderstands a typing pattern, framework API, or other valid Python code in your project, let us know in YouTrack – a small reproducer can help us turn that friction into the next fix.</p>



<p>Try <a href="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm 2026.2</a> and let us know which improvements make the biggest difference for your workflow.</p>



<p>Thank you for using PyCharm!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Hybrid and Local AI course at DeepLearning.AI</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/hybrid-and-local-ai-course-at-deeplearning-ai/</link>
		
		<dc:creator><![CDATA[Paul Everitt]]></dc:creator>
		<pubDate>Thu, 13 Aug 2026 10:01:32 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-social-BlogFeatured-1280x720-1.png</featuredImage>		<product ><![CDATA[education]]></product>
		<category><![CDATA[learning-courses]]></category>
		<category><![CDATA[news]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[local-ai]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=730368</guid>

					<description><![CDATA[Open weight models are having a moment, driven by control, choice, and cost. Hybrid and local AI are now getting serious looks, so JetBrains teamed up with DeepLearning.AI on a free AI Coding Workflows: Hybrid to Local course that covers the ideas and options. The course is now available and uses PyCharm and its AI [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Open weight models are having a moment, driven by control, choice, and cost. Hybrid and local AI are now getting serious looks, so JetBrains teamed up with <a href="https://www.deeplearning.ai" target="_blank" rel="noopener">DeepLearning.AI</a> on a free <a href="https://learn.deeplearning.ai/courses/ai-coding-workflows-from-cloud-to-local/lesson/napfi1/introduction" target="_blank" rel="noopener">AI Coding Workflows: Hybrid to Local</a> course that covers the ideas and options.</p>



<p>The course is now available and uses <a href="https://www.jetbrains.com/pycharm/" data-type="link" data-id="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm</a> and its AI Chat. Here’s a peek into the course.</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Take back control of your AI coding workflow" src="https://www.youtube.com/embed/xUXJuPC7gz0?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<h2 class="wp-block-heading">Claude Code: Subagents and cheaper models</h2>



<p>We start the course with, well, not-local. Instead, we use what you already know – Claude Code and its Anthropic models – to introduce some of the techniques and “levers” that help bring choice, control, and even cost reduction. (Yes, I wrote emdashes.)</p>



<p>We did a previous course on <a href="https://www.deeplearning.ai/courses/spec-driven-development-with-coding-agents" target="_blank" rel="noopener">Spec-Driven Development</a> (SDD) so of course, we wanted to start there. Smaller models struggle with big, open-ended “vibe coding.” Dividing and bounding the work keeps smaller models on track. Important note: this course’s example app is <em>really</em> basic. You might say “that’s too easy.” But that’s part of the takeaway: big brain models can do the upfront work, forming right-sized steps for smaller models.</p>



<p>We then illustrate this division with a Claude Code <em>subagent</em>. The main chat prompt implements each roadmap phase in a fresh subagent, to better manage context. This then gives the payoff: a cheaper model for the implementer. Use a “big brain” (Opus) for main conversation thinking and a “little brain” (Haiku) for implementation.</p>



<p>Each lesson finishes with metrics about the change in tokens, turns, cost, and estimated wall time. Which brings us to the main course goal: learning the <em>ideas</em> instead of the specifics, which change weekly.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/metrics.png" alt="" class="wp-image-730947"/></figure>



<h2 class="wp-block-heading">New agent, inference, and model</h2>



<p>That covers the four levers:</p>



<ul class="wp-block-list">
<li>Specs shaped for the model size</li>



<li>Specialist subagents to divide work</li>



<li>Cheaper models for the routine work</li>



<li>Collect metrics as evidence to guide thinking</li>
</ul>



<p><br>The course then introduces choice and control:</p>



<ul class="wp-block-list">
<li>New agent: OpenCode</li>



<li>New inference router: OpenRouter</li>



<li>New model and inference host: DeepSeek (via OpenRouter) by moving to a new agent (OpenCode) using inference routing (OpenRouter) to inference hosting and models (DeepSeek)</li>
</ul>



<p><br>We first move to OpenCode, running in PyCharm. JetBrains wants our IDEs to be <em>open platforms</em> for agents and models. This makes the move from Claude Code to OpenCode straightforward: it’s the same UI. We add OpenRouter (a paid step), connect it to OpenCode, and choose DeepSeek as a model.</p>



<p>Next we repeat our sequence: all in one chat, then context isolation using a subagent. But this time, with a different agent and model.</p>



<p>We finish by making a dedicated implementer subagent in Markdown. This gives quite a number of levers of control: in the frontmatter for mandatory controls, and in the subagent body for “persuasion” guidance. Most importantly, we have the implementer use the smaller DeepSeek v4 Flash model as the “little brain.”</p>



<p>Compared to the Claude Code version, the metrics were, unsurprisingly, a lot cheaper.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/openrouter.png" alt="" class="wp-image-730958"/></figure>



<h2 class="wp-block-heading">Hybrid and Local</h2>



<p>Now for the main attraction: for routine development, can we do some – or even all – of the work locally?</p>



<p>We start with a lesson on setting up local AI: LM Studio as the inference server and Gemma 4 12B as the local model, targeting a 32 GB laptop.</p>



<p>We then configure the implementer subagent to use this local Gemma 4 model, promoting DeepSeek v4 Flash from last lesson’s “little brain” up to “big brain.” The results? Quite good, as it turns out.</p>



<p>Then the big test: fully local, with Qwen 3.5 27B as the “big brain.” The results: better than expected, showing that guardrails help.</p>



<p>How did hybrid and local do? Both of these lessons finish with a review of the metrics. That’s one of the big course takeaways: look at the evidence. You can see how small models struggle, and see the effect of helping them succeed.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/lmstudio.png" alt="" class="wp-image-730969"/></figure>



<h2 class="wp-block-heading">Hybrid and Local AI Are Heating Up</h2>



<p>Much thanks to <a href="http://deeplearning.ai/" target="_blank" rel="noopener">DeepLearning.AI</a> both for working with us again and for pushing to get this out fast. This topic is now red-hot in the news: Sovereign AI, privacy and security, and of course cost. The innovations are coming really fast and it is important to have a gentle introduction to the fundamentals.</p>



<p>We’ll do more updates here on Local AI for control, choice, and cost. Most of all, we at PyCharm believe in the human-in-the-loop. Stay tuned for more on this.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://learn.deeplearning.ai/courses/ai-coding-workflows-from-cloud-to-local/lesson/napfi1/introduction" class="btn" target="" rel="noopener">Enroll for free</a>
                                                    </div>
    </div>




]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>What&#8217;s New in PyCharm 2026.2.1</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</link>
		
		<dc:creator><![CDATA[Evgenia Verbina]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 12:05:32 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-releases-BlogFeatured-1280x720-1.png</featuredImage>		<category><![CDATA[releases]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=727409</guid>

					<description><![CDATA[This PyCharm release is a big one for anyone building with AI. Your agents can now roll up their sleeves inside your Jupyter notebooks – working against a live kernel instead of firing off disconnected scripts. And they finally know which Python to use, so packages land in the right environment every time. We&#8217;re also [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>This <a href="https://www.jetbrains.com/pycharm/" data-type="link" data-id="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm</a> release is a big one for anyone building with AI. Your agents can now roll up their sleeves inside your Jupyter notebooks – working against a live kernel instead of firing off disconnected scripts. And they finally know which Python to use, so packages land in the right environment every time.</p>



<p>We&#8217;re also welcoming marimo notebooks into the IDE and introducing changes to bundled plugins to keep PyCharm fast and focused.</p>



<h2 class="wp-block-heading">Release highlights</h2>



<h3 class="wp-block-heading">Jupyter notebook skill for AI agents</h3>



<p>Let AI agents such as Claude Code and Codex create, edit, and run <code>.ipynb</code> notebooks via PyCharm&#8217;s notebook model and a live kernel, so variables, models, and data persist across cells instead of disappearing when the agent shells out. For you, this means more reliable notebook and ML work – with fewer tokens used. To start, just open the AI chat and ask the agent to work in your notebook.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://blog.jetbrains.com/pycharm/2026/08/we-gave-ai-agents-a-live-jupyter-kernel-in-pycharm/" class="btn" target="">Learn more</a>
                                                    </div>
    </div>







<h3 class="wp-block-heading">Agent environment coordinator</h3>



<p>Tired of AI agents installing packages into the wrong Python environment? This new skill gives the agent your project&#8217;s configured interpreter and tool – <code>uv</code>, Poetry, <code>pip</code> in a <code>venv</code>, or <code>conda</code> – so commands target the right environment, not a system one. If none exists, it can set one up via PyCharm, and the agent decides how to use the information. To start, ask the agent to run or install something in your project.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://blog.jetbrains.com/pycharm/2026/08/we-stopped-ai-agents-from-installing-into-the-wrong-python-task-success-rates-jumped-to-95/" class="btn" target="">Learn more</a>
                                                    </div>
    </div>







<h3 class="wp-block-heading">marimo notebooks in PyCharm [third-party plugin]</h3>



<p>You can now open, edit, and run marimo notebooks directly in PyCharm with the new plugin developed by the marimo team.&nbsp;</p>



<p>Work with reactive cells and interactive UI elements in a dedicated notebook without leaving your IDE. Because marimo notebooks are stored as Python files, they are Git-friendly, executable as scripts, and easy to integrate into your existing Python projects.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-51.png" alt="" class="wp-image-727410"/></figure>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://plugins.jetbrains.com/plugin/32416-marimo" class="btn" target="" rel="noopener">Install the plugin</a>
                                                    </div>
    </div>







<h3 class="wp-block-heading">Changes to bundled plugins in 2026.2</h3>



<p>As part of ongoing maintenance, we are unbundling and deprecating low-usage plugins, including Data Wrangler, Hugging Face, and Google Colab support. You can continue to install compatible versions from JetBrains Marketplace, but these plugins are no longer bundled or actively maintained by the PyCharm team. A more focused set of bundled plugins means a leaner codebase, helping us keep PyCharm fast and responsive and invest our effort where it has the most impact.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://blog.jetbrains.com/pycharm/2026/08/unbundling-and-deprecating-low-usage-plugins-in-pycharm/" class="btn" target="">Learn more</a>
                                                    </div>
    </div>







<h2 class="wp-block-heading">Redesigned Python Packages tool window</h2>



<ul class="wp-block-list">
<li>Packages now appear in a collapsible tree alongside their dependencies so you can see what&#8217;s installed and why – with new icons, right-aligned versions, and inline Install/Update links.</li>



<li>A new floating search popup (think “Search Everywhere” but for packages) makes finding and installing fast. It also shows you which environment will be used and lets you pick a module and dependency group.</li>



<li>You can install a package into specific <code>uv</code>/Poetry dependency groups like dev or test per workspace member, change versions inline or through the new Change Version dialog, and install from VCS via Custom Installation.</li>



<li>Package repositories can be enabled or disabled, with state remembered across sessions. In addition, unreachable URLs show a clear error, and Remote Development support is substantially improved.</li>
</ul>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/PY-89838-Python-Packages-Tool-Window-redesign.png" alt="Redesigned Python Packages tool window in PyCharm" class="wp-image-730282"/></figure>



<h2 class="wp-block-heading">Clearer type checking</h2>



<p>Get clearer, more actionable type messages:</p>



<ul class="wp-block-list">
<li>Richer type-mismatch errors, now with a breakdown of why the types don&#8217;t match. </li>



<li>A type diff for callables and other composite types, so both sides read the same way. </li>



<li>Fully rendered names and types in inspection tooltips, with clickable links. </li>
</ul>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/08/PY-80221-explain_Show-a-breakdown-when-type-mismatch-is-reported.png" alt="Clearer type checking in PyCharm" class="wp-image-730293"/></figure>



<h2 class="wp-block-heading">Bug fixes</h2>



<ul class="wp-block-list">
<li><strong>Virtual environments for end-of-life Python</strong>: PyCharm no longer creates venvs for Python 2.7, 3.6, and 3.7. You can still create a new env from a command line and add it to the IDE manually.</li>



<li><strong>SQLAlchemy 2.0 Session.get() inference</strong>: <code>Session.get(Entity, id)</code> (and SQLModel) is now inferred as a model instance rather than the class.</li>
</ul>



<h2 class="wp-block-heading">Download PyCharm</h2>



<p>All of these updates are available in PyCharm 2026.2.1. Update right from the IDE or the Toolbox App, or <a href="https://www.jetbrains.com/pycharm/download/" target="_blank" rel="noopener">download the latest version</a> to try everything out on your own projects. As always, we’d love to hear your feedback.</p>


    <div class="buttons">
        <div class="buttons__row">
                                                <a href="https://www.jetbrains.com/pycharm/download/" class="btn" target="" rel="noopener">Download PyCharm</a>
                                                    </div>
    </div>







<p></p>
]]></content:encoded>
					
		
		
		                    <language>
                        <code><![CDATA[zh-hans]]></code>
                        <url>https://blog.jetbrains.com/zh-hans/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[pt-br]]></code>
                        <url>https://blog.jetbrains.com/pt-br/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[ko]]></code>
                        <url>https://blog.jetbrains.com/ko/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[ja]]></code>
                        <url>https://blog.jetbrains.com/ja/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[fr]]></code>
                        <url>https://blog.jetbrains.com/fr/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[es]]></code>
                        <url>https://blog.jetbrains.com/es/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                                    <language>
                        <code><![CDATA[de]]></code>
                        <url>https://blog.jetbrains.com/de/pycharm/2026/08/what-s-new-in-pycharm-2026-2-1/</url>
                    </language>
                	</item>
		<item>
		<title>We Stopped AI Agents From Installing Into the Wrong Python: Task Success Rates Jumped to 95%+</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/we-stopped-ai-agents-from-installing-into-the-wrong-python-task-success-rates-jumped-to-95/</link>
		
		<dc:creator><![CDATA[Evgenia Verbina]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 12:01:07 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-releases-BlogFeatured-1280x720-1-3.png</featuredImage>		<category><![CDATA[releases]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=730210</guid>

					<description><![CDATA[AI agents are supposed to save you time. Ask one to install a dependency or run your project, though, and it often does the opposite: It installs into the wrong Python, ignores the uv or virtual environment your project uses, and hands back a broken setup for you to fix yourself. PyCharm’s new Agent Environment [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>AI agents are supposed to save you time. Ask one to install a dependency or run your project, though, and it often does the opposite: It installs into the wrong Python, ignores the uv or virtual environment your project uses, and hands back a broken setup for you to fix yourself.</p>



<p><a href="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm</a>’s new Agent Environment Coordinator skill fixes this, and this blog post shows just how helpful it proves to be.</p>



<figure style="margin: 28px 0;">
  <svg viewBox="0 0 780 220" role="img" aria-label="Opener visualization for the Agent Environment Coordinator skill. Average task success improved from 68 percent at baseline to 98 percent with the skill, across 28 Python tasks and 6 AI models, while avoiding system Python pollution." style="display:block;width:100%;height:auto;max-width:780px;">
    <rect x="0" y="0" fill="#ffffff"/>
    <rect x="18" y="18" fill="#ffffff" stroke="#d1d5db" rx="6"/>

    <text x="42" y="52" font-size="12" font-weight="700" fill="#6b7280">AGENT ENVIRONMENT COORDINATOR</text>
    <text x="42" y="82" font-size="24" font-weight="750" fill="#111827">The agent stopped guessing Python.</text>

    <text x="44" y="116" font-size="15" font-weight="700" fill="#374151">Average task success</text>
    <text x="42" y="170" font-size="50" font-weight="800" fill="#111827">68%</text>
    <text x="170" y="168" font-size="24" font-weight="700" fill="#9ca3af">-&gt;</text>
    <text x="228" y="170" font-size="50" font-weight="800" fill="#16a34a">98%</text>
    <text x="44" y="194" font-size="12" font-weight="700" fill="#6b7280">Baseline</text>
    <text x="230" y="194" font-size="12" font-weight="700" fill="#047857">With skill</text>

    <rect x="482" y="48" fill="#f8fafc" stroke="#e5e7eb" rx="6"/>
    <text x="504" y="72" font-size="22" font-weight="800" fill="#111827">28</text>
    <text x="504" y="91" font-size="12" fill="#6b7280">Python tasks</text>

    <rect x="610" y="48" fill="#f8fafc" stroke="#e5e7eb" rx="6"/>
    <text x="632" y="72" font-size="22" font-weight="800" fill="#111827">6</text>
    <text x="632" y="91" font-size="12" fill="#6b7280">AI models</text>

    <rect x="482" y="118" fill="#ecfdf5" stroke="#bbf7d0" rx="6"/>
    <circle cx="508" cy="141" r="8" fill="#16a34a"/>
    <path d="M504 141 L507 144 L513 137" fill="none" stroke="#ffffff" stroke- stroke-linecap="round" stroke-linejoin="round"/>
    <text x="528" y="145" font-size="13" font-weight="700" fill="#047857">No system Python pollution</text>
  </svg>
</figure>



<p>We tested six AI models using 28 different Python programming tasks. Without access to the project&#8217;s real environment, they solved 68% of the tasks on average. After we gave them access, their average success rate shot up to 98% – and they didn’t even modify the system Python.</p>



<p>If you’re currently using AI agents in your Python projects, read on to see how the Agent Environment Coordinator can improve their performance.</p>



<h2 class="wp-block-heading">When the agent could see the project&#8217;s environment, it stopped failing</h2>



<p>When using the Agent Environment Coordinator skill, each agent, regardless of the model, was able to complete far more of the 28 tasks. (See the Methodology section below for details on what the tasks entailed.) Here is the share of successfully completed tasks for each model, comparing the baseline to running with the skill in PyCharm:</p>



<figure style="margin: 24px 0;">
  <svg viewBox="0 0 780 330" role="img" aria-label="Model success rates without and with the Agent Environment Coordinator skill. Claude Sonnet 4.6: 36 percent to 96 percent. Claude Sonnet 5: 73 percent to 100 percent. Claude Opus 4.8: 67 percent to 100 percent. Claude Opus 5.0: 94 percent to 98 percent. Codex GPT-5.5: 62 percent to 95 percent. Codex GPT-5.6: 80 percent to 100 percent." style="display:block;width:100%;height:auto;max-width:780px;">
    <rect x="0" y="0" fill="#ffffff"/>
    <text x="24" y="28" font-size="11" font-weight="700" fill="#6b7280">MODEL</text>
    <text x="292" y="28" font-size="11" font-weight="700" fill="#6b7280">BASELINE</text>
    <text x="535" y="28" font-size="11" font-weight="700" fill="#6b7280">WITH SKILL</text>
    <line x1="20" y1="44" x2="760" y2="44" stroke="#d1d5db"/>
    <line x1="20" y1="90" x2="760" y2="90" stroke="#e5e7eb"/>
    <line x1="20" y1="136" x2="760" y2="136" stroke="#e5e7eb"/>
    <line x1="20" y1="182" x2="760" y2="182" stroke="#e5e7eb"/>
    <line x1="20" y1="228" x2="760" y2="228" stroke="#e5e7eb"/>
    <line x1="20" y1="274" x2="760" y2="274" stroke="#e5e7eb"/>
    <line x1="20" y1="320" x2="760" y2="320" stroke="#d1d5db"/>
    <text x="24" y="73" font-size="14" font-weight="700" fill="#111827">Claude Sonnet 4.6</text>
    <rect x="292" y="60" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="60" fill="#6b7280" rx="3"/>
    <text x="452" y="73" font-size="13" font-weight="700" fill="#111827">36%</text>
    <rect x="535" y="60" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="60" fill="#16a34a" rx="3"/>
    <text x="695" y="73" font-size="13" font-weight="700" fill="#111827">96%</text>
    <text x="24" y="119" font-size="14" font-weight="700" fill="#111827">Claude Sonnet 5</text>
    <rect x="292" y="106" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="106" fill="#6b7280" rx="3"/>
    <text x="452" y="119" font-size="13" font-weight="700" fill="#111827">73%</text>
    <rect x="535" y="106" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="106" fill="#16a34a" rx="3"/>
    <text x="695" y="119" font-size="13" font-weight="700" fill="#111827">100%</text>
    <text x="24" y="165" font-size="14" font-weight="700" fill="#111827">Claude Opus 4.8</text>
    <rect x="292" y="152" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="152" fill="#6b7280" rx="3"/>
    <text x="452" y="165" font-size="13" font-weight="700" fill="#111827">67%</text>
    <rect x="535" y="152" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="152" fill="#16a34a" rx="3"/>
    <text x="695" y="165" font-size="13" font-weight="700" fill="#111827">100%</text>
    <text x="24" y="211" font-size="14" font-weight="700" fill="#111827">Claude Opus 5.0</text>
    <rect x="292" y="198" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="198" fill="#6b7280" rx="3"/>
    <text x="452" y="211" font-size="13" font-weight="700" fill="#111827">94%</text>
    <rect x="535" y="198" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="198" fill="#16a34a" rx="3"/>
    <text x="695" y="211" font-size="13" font-weight="700" fill="#111827">98%</text>
    <text x="24" y="257" font-size="14" font-weight="700" fill="#111827">Codex / GPT-5.5</text>
    <rect x="292" y="244" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="244" fill="#6b7280" rx="3"/>
    <text x="452" y="257" font-size="13" font-weight="700" fill="#111827">62%</text>
    <rect x="535" y="244" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="244" fill="#16a34a" rx="3"/>
    <text x="695" y="257" font-size="13" font-weight="700" fill="#111827">95%</text>
    <text x="24" y="303" font-size="14" font-weight="700" fill="#111827">Codex / GPT-5.6</text>
    <rect x="292" y="290" fill="#f3f4f6" rx="3"/>
    <rect x="292" y="290" fill="#6b7280" rx="3"/>
    <text x="452" y="303" font-size="13" font-weight="700" fill="#111827">80%</text>
    <rect x="535" y="290" fill="#f3f4f6" rx="3"/>
    <rect x="535" y="290" fill="#16a34a" rx="3"/>
    <text x="695" y="303" font-size="13" font-weight="700" fill="#111827">100%</text>
  </svg>
</figure>



<p>Every model improved, with the weakest baseline improving the most.</p>



<h2 class="wp-block-heading">Why we built this</h2>



<p>LLMs almost never use a project&#8217;s dedicated virtual environment. They fall back to a system interpreter, ignoring the fact that there may be several system interpreters and real projects often have more complex, multi-interpreter setups already configured in PyCharm that the agent has no way to see.</p>



<p>For example, <code>pip install httpx</code> runs against the wrong Python, the package installs globally, the script fails, and the environment is polluted.</p>



<p>PyCharm already knows which interpreter belongs to your project and which tool manages it. The agent just couldn’t ask – so we gave it a way.</p>



<h2 class="wp-block-heading">How it works</h2>



<p>The Agent Environment Coordinator lets the agent ask PyCharm two things. <code>get_python_environment</code> returns the correct interpreter for the file or module in question – the path plus the tool behind it (<code>uv</code>, Poetry, <code>pip</code> + venv, conda). If no environment exists yet, <code>configure_python_interpreter</code> sets one up by reusing PyCharm&#8217;s existing configuration mechanism – the same one that offers to create a <code>.venv</code> – so the new interpreter also becomes visible in the IDE.</p>



<p>The important part is what the skill <em>doesn&#8217;t </em>do. It returns information; it never intercepts or rewrites the command. The agent asks which Python to use, gets an accurate answer, and decides whether and how to use it to write the command itself. We hand it the missing context using existing mechanisms in PyCharm – we don&#8217;t let it take the wheel.</p>



<p>The payoff is practical: The agent works with your project setup out of the box. You don’t need to coach it through prompts about which environment to use, or clean up wrong installs afterward.</p>



<h2 class="wp-block-heading">Methodology</h2>



<p>We built a dataset of 28 tasks covering everyday Python-environment work, like running tests, installing a library, listing dependencies, resolving a version conflict, and so forth.</p>



<p>Each task ultimately required the agent to pick the correct interpreter to execute a command. The eval also reduced the reward when the agent polluted the system environment, so a high score reflects a clean run, not just a passing one.</p>



<p>We ran the full set three times per model, with and without the skill, using <a href="https://www.harborframework.com/" target="_blank" rel="noopener">Harbor</a>, and averaged the results.</p>



<h2 class="wp-block-heading">Results</h2>



<p>Success rates climbed across the board – Sonnet 5 improved from 73% to 100%, Opus 5 from 94% to 100%, and Codex/GPT-5.6 from 80% to 100%.&nbsp;</p>



<p>Two things stand out in addition to this numerical jump:&nbsp;</p>



<ul class="wp-block-list">
<li>The improved success rates demonstrate that the models lacked context, rather than being incapable of completing the tasks. The models didn&#8217;t get better – they just stopped guessing the interpreter, which is why the weakest baseline improved the most.</li>



<li>Because the eval docks points for polluting the system environment, these higher scores also imply cleaner runs. The agents didn&#8217;t just pass more often; they stopped leaving a mess behind.</li>
</ul>



<h2 class="wp-block-heading">Want to try it?</h2>



<p>Open the AI chat in <a href="https://www.jetbrains.com/pycharm/download/" data-type="link" data-id="https://www.jetbrains.com/pycharm/download/" target="_blank" rel="noopener">PyCharm 2026.2.1</a> and ask your agent to install a package or run something in your project – it&#8217;ll reach for the right interpreter on its own.</p>



<p>The Agent Environment Coordinator is one of PyCharm&#8217;s bundled skills. You can browse and manage all of them right in the IDE, expand the built-in library with external registries like public GitHub repositories, or import skills you&#8217;ve already set up for Claude Code or Codex.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/skillsManager.mp4"></video></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>We Gave AI Agents a Live Jupyter Kernel in PyCharm</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/we-gave-ai-agents-a-live-jupyter-kernel-in-pycharm/</link>
		
		<dc:creator><![CDATA[Evgenia Verbina]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 12:00:34 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-releases-BlogFeatured-1280x720-1-2.png</featuredImage>		<category><![CDATA[releases]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=730172</guid>

					<description><![CDATA[If you&#8217;ve handed notebook work to an AI agent, you know how it tends to go: More often than not, it corrupts your .ipynb, loses your trained model the moment the run finishes, or burns budget sitting idle through a long job while you watch. To solve this, we’re introducing a brand-new Jupyter skill. Built [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>If you&#8217;ve handed notebook work to an AI agent, you know how it tends to go: More often than not, it corrupts your <code>.ipynb</code>, loses your trained model the moment the run finishes, or burns budget sitting idle through a long job while you watch.</p>



<p>To solve this, we’re introducing a brand-new Jupyter skill. Built directly into <a href="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm,</a> it lets your AI agent work inside a live Jupyter kernel instead of handing the job to a subprocess and losing your progress. This one change means state persists across cells, the <code>.ipynb</code> isn&#8217;t corrupted, and long jobs wait until execution is completed instead of constantly checking and wasting precious tokens.</p>



<figure style="margin: 28px 0;">
  <svg viewBox="0 0 780 280" role="img" aria-label="Opener visualization for the Jupyter skill. Claude Opus 5 ran 12 percent cheaper through the live kernel than through the shell: USD 59.09 versus USD 67.06 across 12 machine learning tasks. Kernel mode had 98 percent cache reads, and state persists across cells." style="display:block;width:100%;height:auto;max-width:780px;">
    <rect x="0" y="0" fill="#ffffff"/>
    <rect x="18" y="18" fill="#ffffff" stroke="#d1d5db" rx="6"/>

    <text x="42" y="52" font-size="12" font-weight="700" fill="#6b7280">JUPYTER SKILL FOR PYCHARM</text>
    <text x="42" y="82" font-size="24" font-weight="750" fill="#111827">A live kernel made Opus</text>
    <text x="42" y="111" font-size="24" font-weight="750" fill="#111827">cheaper than the shell.</text>

    <text x="42" y="170" font-size="50" font-weight="800" fill="#16a34a">12%</text>
    <text x="153" y="170" font-size="50" font-weight="800" fill="#16a34a">cheaper</text>
    <text x="44" y="194" font-size="13" fill="#6b7280">Claude Opus 5 across 12 ML tasks</text>

    <rect x="42" y="216" fill="#f3f4f6" rx="3"/>
    <rect x="42" y="216" fill="#16a34a" rx="3"/>
    <text x="42" y="246" font-size="12" font-weight="700" fill="#047857">Kernel</text>
    <text x="96" y="246" font-size="12" fill="#047857">USD 59.09</text>

    <rect x="362" y="216" fill="#f3f4f6" rx="3"/>
    <rect x="362" y="216" fill="#6b7280" rx="3"/>
    <text x="362" y="246" font-size="12" font-weight="700" fill="#374151">Shell</text>
    <text x="411" y="246" font-size="12" fill="#6b7280">USD 67.06</text>

    <rect x="502" y="48" fill="#f8fafc" stroke="#e5e7eb" rx="6"/>
    <text x="520" y="72" font-size="22" font-weight="800" fill="#111827">12</text>
    <text x="520" y="91" font-size="12" fill="#6b7280">ML tasks</text>

    <rect x="612" y="48" fill="#ecfdf5" stroke="#bbf7d0" rx="6"/>
    <text x="632" y="72" font-size="22" font-weight="800" fill="#16a34a">98%</text>
    <text x="632" y="91" font-size="12" fill="#047857">cache reads</text>

    <rect x="502" y="122" fill="#f8fafc" stroke="#e5e7eb" rx="6"/>
    <circle cx="526" cy="145" r="8" fill="#16a34a"/>
    <path d="M522 145 L525 148 L531 141" fill="none" stroke="#ffffff" stroke- stroke-linecap="round" stroke-linejoin="round"/>
    <text x="546" y="149" font-size="13" font-weight="700" fill="#111827">State persists across cells</text>
  </svg>
</figure>



<h2 class="wp-block-heading">For Opus, the kernel ran cheaper than the shell</h2>



<p>We tested the efficiency of the Jupyter skill by comparing the performance of agents when solving twelve different machine learning problems. We compared three different modes: strictly using bash, strictly using the kernel via the Jupyter skill, and a mixture of both.</p>



<p>While the agent was able to solve all twelve tasks in every mode, there was a difference in how much each mode spent. For Claude Opus 5, working through the kernel cost 59.09 USD versus 67.06 USD through the shell – about 12% cheaper.</p>



<figure style="margin: 24px 0;">
  <svg viewBox="0 0 780 170" role="img" aria-label="Table visualization. Kernel: cost USD 59.09, input 72.7M, cache reads 98 percent. Shell: cost USD 67.06, input 36.3M, cache reads 82 percent." style="display:block;width:100%;height:auto;max-width:780px;">
    <rect x="0" y="0" fill="#ffffff"/>
    <text x="24" y="28" font-size="11" font-weight="700" fill="#6b7280">MODE</text>
    <text x="170" y="28" font-size="11" font-weight="700" fill="#6b7280">COST</text>
    <text x="390" y="28" font-size="11" font-weight="700" fill="#6b7280">INPUT TOKENS</text>
    <text x="585" y="28" font-size="11" font-weight="700" fill="#6b7280">CACHE READS</text>
    <line x1="20" y1="44" x2="760" y2="44" stroke="#d1d5db"/>
    <line x1="20" y1="104" x2="760" y2="104" stroke="#e5e7eb"/>
    <line x1="20" y1="164" x2="760" y2="164" stroke="#d1d5db"/>
    <text x="24" y="78" font-size="16" font-weight="700" fill="#111827">Kernel (skill)</text>
    <text x="24" y="138" font-size="16" font-weight="700" fill="#111827">Shell (baseline)</text>
    <rect x="170" y="58" fill="#f3f4f6" rx="3"/>
    <rect x="170" y="58" fill="#16a34a" rx="3"/>
    <text x="170" y="90" font-size="13" font-weight="700" fill="#111827">USD 59.09</text>
    <rect x="170" y="118" fill="#f3f4f6" rx="3"/>
    <rect x="170" y="118" fill="#6b7280" rx="3"/>
    <text x="170" y="150" font-size="13" font-weight="700" fill="#111827">USD 67.06</text>
    <rect x="390" y="58" fill="#f3f4f6" rx="3"/>
    <rect x="390" y="58" fill="#16a34a" rx="3"/>
    <text x="390" y="90" font-size="13" font-weight="700" fill="#111827">72.7M</text>
    <rect x="390" y="118" fill="#f3f4f6" rx="3"/>
    <rect x="390" y="118" fill="#6b7280" rx="3"/>
    <text x="390" y="150" font-size="13" font-weight="700" fill="#111827">36.3M</text>
    <rect x="585" y="58" fill="#f3f4f6" rx="3"/>
    <rect x="585" y="58" fill="#16a34a" rx="3"/>
    <text x="585" y="90" font-size="13" font-weight="700" fill="#111827">98%</text>
    <rect x="585" y="118" fill="#f3f4f6" rx="3"/>
    <rect x="585" y="118" fill="#6b7280" rx="3"/>
    <text x="585" y="150" font-size="13" font-weight="700" fill="#111827">82%</text>
  </svg>
</figure>



<p>Here’s the counterintuitive part: The kernel used more tokens, yet cost less. That&#8217;s because it keeps the prompt cache warm. 98% of its input was cache reads, versus 82% for the shell – and cache reads incur only 1/12 of the cost of creating a fresh cache.</p>



<h2 class="wp-block-heading">Why we built this</h2>



<p>Notebooks are where coding agents tend to fall apart. Most AI tools treat an <code>.ipynb</code> like a plain text file: They hand-edit the JSON (and corrupt it), and then run code by running a subprocess. The moment an agent starts the subprocess, the kernel state – the trained model, the loaded dataframe, and every import – lives in the child process, and vanishes when that process exits. The agent can&#8217;t inspect it, checkpoint it, or reuse it. Output is buffered until the run ends, so progress is invisible, and long training jobs get babysat – blind until the connection times out.</p>



<p>We asked the obvious question: What if the agent operated a live Jupyter kernel through the IDE?</p>



<p>So we built our new Jupyter skill, which exposes PyCharm&#8217;s own notebook intelligence – its notebook model and live-kernel control – to the agent. It does this through a single MCP wrapper, <code>execute_tool</code>, which covers the core notebook operations, including creating, editing, and reading notebooks; running cells; waiting on long runs; probing a running kernel; and controlling its lifecycle. The skill tells the agent when and how to use them.</p>



<h2 class="wp-block-heading">How it works</h2>



<p>The agent:</p>



<ul class="wp-block-list">
<li><strong>Runs directly in the kernel.</strong> The agent writes real Python into a cell and runs it, so variables, models, and data persist across cells – exactly like a human working in a notebook.</li>



<li><strong>Waits instead of polling.</strong> Rather than polling on a fixed timer and re-billing context on every idle call, <code>wait_cell_execution</code> is blocked until the cell finishes (or a safe cap), and then hands control back. This helps reduce idle round-trips.</li>



<li><strong>Reads only what&#8217;s new.</strong> As a long run streams output, the agent reads the <em>delta</em> – just the lines since its last check – instead of re-sending the whole, ever-growing cell output every time.</li>
</ul>



<h2 class="wp-block-heading">Methodology</h2>



<p>We used twelve tasks from the MLGym machine-learning benchmark – classification, regression, and reinforcement-learning problems, each of which requires the agent to load data, train, evaluate, and save a result. We ran them across Claude Opus 5 and OpenAI&#8217;s GPT-5.6 models, Sol and Terra, through Codex. We compared three modes: through the kernel only, through the kernel plus the shell, and through the shell alone. As these benchmark tasks expose test labels to the agent, we treat cost – not accuracy – as the reliable signal.</p>



<p>One caveat, for transparency: An audit found that one of the twelve tasks, Titanic, was contaminated – the agent could peek at the test set, and each agent used this to select the best model to present as the final solution. Titanic is a well-known, easy task for LLMs, and the issue appeared consistently across all three modes, so it doesn&#8217;t skew the comparison. The pattern holds even with Titanic removed – the kernel still ran 10% cheaper than the shell for Opus (56.34 USD versus 62.65 USD).</p>



<h2 class="wp-block-heading">Results</h2>



<p>The cost win is model- and task-dependent. It was clearest for Claude Opus on long, stateful jobs, while the shell came out cheaper on short tasks and for the Codex models – which already use the cache efficiently, so there the skill earns its place on workflow, not cost.</p>



<h2 class="wp-block-heading">Where it still falls short</h2>



<p>Two things are worth keeping in mind:</p>



<ul class="wp-block-list">
<li><strong>Tell the agent to save its artifacts.</strong> In one run, the agent trained a solid model but never saved the submission file before finishing. This is easy to prevent from your side: Just add a clear instruction in your context file (e.g. <code>CLAUDE.md</code>) or a skill so the agent saves any model the moment it clears your target metric.</li>



<li><strong>Some tasks are still beyond agents.</strong> On a hard task, the agent&#8217;s approach simply wasn&#8217;t strong enough to clear the bar. That&#8217;s genuine ML difficulty, not a tooling gap – some complex problems still need a human in the loop.</li>
</ul>



<p>The skill removes the <em>mechanical</em> waste, but doesn&#8217;t turn a weak approach into a strong one.</p>



<h2 class="wp-block-heading">Want to try it?</h2>



<p>Open the AI chat in <a href="https://www.jetbrains.com/pycharm/download/" data-type="link" data-id="https://www.jetbrains.com/pycharm/download/" target="_blank" rel="noopener">PyCharm 2026.2.1</a> and ask your agent to work in a notebook – create one, load a dataset, or kick off a training run. The agent will operate the kernel directly instead of running commands in the shell.</p>



<p>You can also browse and manage skills directly from the IDE, expand the built-in library with external registries like public GitHub repositories, or let PyCharm import skills you’ve already set up for Claude Code or Codex.</p>



<figure class="wp-block-video"><video controls src="https://blog.jetbrains.com/wp-content/uploads/2026/08/skillsManager.mp4"></video></figure>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Unbundling and Deprecating Low-Usage Plugins in PyCharm</title>
		<link>https://blog.jetbrains.com/pycharm/2026/08/unbundling-and-deprecating-low-usage-plugins-in-pycharm/</link>
		
		<dc:creator><![CDATA[Evgenia Verbina]]></dc:creator>
		<pubDate>Wed, 12 Aug 2026 11:59:50 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/08/PC-releases-BlogFeatured-1280x720-1-1.png</featuredImage>		<category><![CDATA[releases]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=730146</guid>

					<description><![CDATA[As part of ongoing maintenance, we are unbundling and deprecating low-usage plugins starting with PyCharm 2026.2. This includes support for Data Wrangler, Hugging Face, and Google Colab, among others.&#160; A more focused set of bundled plugins means a leaner codebase, enabling us to keep PyCharm fast and responsive and invest our effort where it has [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>As part of ongoing maintenance, we are unbundling and deprecating low-usage plugins starting with <a href="https://www.jetbrains.com/pycharm/" target="_blank" rel="noopener">PyCharm</a> 2026.2. This includes support for Data Wrangler, Hugging Face, and Google Colab, among others.&nbsp;</p>



<p>A more focused set of bundled plugins means a leaner codebase, enabling us to keep PyCharm fast and responsive and invest our effort where it has the most impact.</p>



<p>You can continue installing compatible versions from the <a href="https://plugins.jetbrains.com/" target="_blank" rel="noopener">JetBrains Marketplace</a>, but these plugins will no longer be bundled or actively maintained by the PyCharm team. Read this blog post for the full list, deprecation timeline, and next steps.</p>



<h2 class="wp-block-heading">Why we’re making this change</h2>



<p>The tools and workflows developers rely on keep evolving, and several of these plugins never reached the level of adoption we hoped for. After reviewing usage trends, we’ve decided to move a set of low-usage plugins out of active development, so our team can focus on features with broader impact for Python developers.</p>



<p>A smaller set of bundled plugins also means a leaner, more maintainable codebase. As PyCharm continues to grow, we want to invest our engineering effort where it has the most impact and keep the IDE fast and responsive over time.</p>



<p>Unbundling and deprecating a plugin doesn’t necessarily mean deleting it. If a certain plugin’s functionality is still used, we’ll move that plugin’s code to a separate <a href="https://github.com/JetBrains/intellij-obsolete-plugins" target="_blank" rel="noopener">Obsolete Plugins repository</a>. The plugin will remain searchable and installable on JetBrains Marketplace with a fixed compatibility range, but will no longer be rebuilt with every new release or maintained by the PyCharm team.</p>



<h2 class="wp-block-heading">Which plugins are affected</h2>



<p>The following plugins are being deprecated; those currently bundled will be unbundled first:</p>



<ul class="wp-block-list">
<li>Data Wrangler</li>



<li>Hugging Face</li>



<li>Google Colab (Jupyter Notebook Colab)</li>



<li>Spark, including PySpark support</li>



<li>AI Playground</li>



<li>AI Agents Debugger</li>



<li>dbt</li>



<li>Databricks</li>
</ul>



<p>Other low-usage plugins may be deprecated in the same way in future releases.</p>



<h2 class="wp-block-heading">Timeline and what to expect</h2>



<p><strong>v2026.2</strong></p>



<ul class="wp-block-list">
<li>The bundled plugins listed above will be unbundled from PyCharm.</li>



<li>The PyCharm team will no longer develop new features for these plugins or maintain them.</li>



<li>Compatible versions will remain available for installation from JetBrains Marketplace and stay compatible with v2026.2.</li>



<li>The plugin source will be published in the Obsolete Plugins repository, where you can continue to build and install it manually.</li>
</ul>



<p><strong>v2026.3 and beyond</strong></p>



<ul class="wp-block-list">
<li>The PyCharm team will no longer publish compatible versions of these plugins starting from v2026.3.</li>
</ul>



<h2 class="wp-block-heading">What this means for you</h2>



<p>If you rely on any of these plugins, you can continue to install a compatible version from <a href="https://plugins.jetbrains.com/" target="_blank" rel="noopener">JetBrains Marketplace</a> for PyCharm 2026.2. Because the source moves to the <a href="https://github.com/JetBrains/intellij-obsolete-plugins" target="_blank" rel="noopener">Obsolete Plugins repository</a> under an open model, the community can keep building and installing the plugins manually. If you’re interested in maintaining one of them, we’d love to hear from you.</p>



<h2 class="wp-block-heading">Thank you</h2>



<p>We’re grateful to everyone who used these plugins, filed issues, and shared feedback over the years. Thank you!</p>



<p>The PyCharm team</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PyTorch Tutorial for Deep Learning</title>
		<link>https://blog.jetbrains.com/pycharm/2026/07/pytorch-tutorial-for-deep-learning/</link>
		
		<dc:creator><![CDATA[Evgenia Verbina]]></dc:creator>
		<pubDate>Wed, 29 Jul 2026 16:18:55 +0000</pubDate>
		<featuredImage>https://blog.jetbrains.com/wp-content/uploads/2026/07/PC-social-BlogFeatured-1280x720-1-4.png</featuredImage>		<category><![CDATA[data-science]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[pytorch]]></category>
		<guid isPermaLink="false">https://blog.jetbrains.com/?post_type=pycharm&#038;p=726160</guid>

					<description><![CDATA[This is a guest post from Naa Ashiorkor, a data scientist and tech community builder. Building intelligent systems that can see, hear, understand language, and make decisions was previously the domain of specialized researchers with massive computing resources only – today, deep learning has made this accessible to developers and data scientists across the world, [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p><em>This is a guest post from <strong><a href="https://blog.jetbrains.com/pycharm/2026/07/pytorch-tutorial-for-deep-learning/#author" data-type="link" data-id="https://blog.jetbrains.com/pycharm/2026/07/pytorch-tutorial-for-deep-learning/#author">Naa Ashiorkor,</a></strong> a data scientist and tech community builder.</em></p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/PC-social-BlogFeatured-1280x720-1-4.png" alt="" class="wp-image-726986"/></figure>



<p>Building intelligent systems that can see, hear, understand language, and make decisions was previously the domain of specialized researchers with massive computing resources only – today, deep learning has made this accessible to developers and data scientists across the world, bringing the ability to build, train, and deploy AI models within reach. </p>



<p>This accessibility can be credited to deep learning frameworks, and one such framework is <a href="https://pytorch.org/" target="_blank" rel="noopener">PyTorch</a>, which has rapidly become the prevailing choice across both research and industry. PyTorch is an open-source deep learning framework built in Python and designed to make building neural networks intuitive.&nbsp;</p>



<p>Curious about how neural networks actually learn? In this tutorial, you’ll build your first PyTorch model using the MNIST dataset in PyCharm and see it recognize handwritten digits in real time. Along the way, you’ll get familiar with tensors and understand the core workflow behind building deep learning models.</p>



<h2 class="wp-block-heading">What is PyTorch?</h2>



<p>PyTorch traces its roots to Torch, a scientific computing framework that used Lua; in 2016, researchers at Facebook’s AI Research Lab (FAIR), now Meta AI, reinvented it for Python, creating PyTorch, which is today a <a href="https://www.linuxfoundation.org/blog/blog/welcoming-pytorch-to-the-linux-foundation" target="_blank" rel="noopener">Linux Foundation community project</a>.&nbsp;</p>



<p>By <a href="https://pytorch.org/blog/2024-year-in-review/" target="_blank" rel="noopener">2024</a>, PyTorch had established itself as the most popular deep learning framework, with a 63% adoption rate in the model training space, used in over 70% of AI research implementations. In <a href="https://pytorch.org/blog/pytorch-foundation-in-2025-a-year-in-review/" target="_blank" rel="noopener">2025</a>, the PyTorch Foundation’s ecosystem grew to include large-scale projects such as vLLM, DeepSpeed, and Ray, all of which are governed independently. </p>



<p>The annual PyTorch Conference attracted more than 3,400 attendees and gained 16 new industry members, including Snowflake, Dell Technologies, and Qualcomm. Also, it is trusted in production by organizations such as Meta, Microsoft, OpenAI, and Tesla. For developers and data scientists looking to enter deep learning, PyTorch remains the most practical and widely supported starting point available today.&nbsp;</p>



<p>PyTorch was built on two foundations: GPU-accelerated tensor computation as a more powerful alternative to NumPy and an automatic differentiation engine for training neural networks. </p>



<p>From these foundations, PyTorch has grown into one of the most fully featured deep learning frameworks available. Its core features include:</p>



<ul class="wp-block-list">
<li><strong>Dynamic computation graphs (define-by-run):</strong> As code executes, PyTorch builds computation graphs. These are maps of every mathematical operation your model performs: things like multiplying inputs by weights, adding biases, and applying activation functions. PyTorch needs to track these because training requires working backwards through all of those steps to calculate how much each weight contributed to the model&#8217;s error, so it knows how to adjust them to improve. Computation graphs allow the model structure to be modified during runtime and facilitate debugging using standard Python tools, making PyTorch ideal for research and experimentation.</li>



<li><strong>Pythonic and intuitive interface:</strong> PyTorch code is Pythonic, which reduces the learning curve. It uses standard Python control flow and clean, readable syntax, and it integrates well with Pythonic libraries.</li>



<li><strong>Strong GPU acceleration:</strong> PyTorch has seamless support for GPUs using CUDA. There is easy device switching and efficient tensor computations on GPUs. It also supports multi-GPU training.</li>



<li><strong>Autograd (automatic differentiation):</strong> There is a built-in autograd engine that automatically computes gradients. It tracks operations on tensors and enables backpropagation with minimal code.</li>



<li><strong>Rich neural network library:</strong> PyTorch provides a comprehensive module for building models. There are prebuilt layers, loss functions, activation functions, and a modular design for custom architectures.&nbsp;</li>



<li><strong>Extensive ecosystem:</strong> PyTorch is not just a framework – it is an ecosystem. There is a wide array of tools, even beyond the AI-specific libraries. Hence, an entire AI project can be managed under the Python umbrella from data collection to deployment.</li>



<li><strong>Model deployment support:</strong> PyTorch supports deploying models from research to production, with support for both mobile and edge deployments. What’s more, it also has TorchScript for optimized execution and ONNX export for interoperability.</li>



<li><strong>Broad community and industry adoption:</strong> PyTorch is backed by Meta, and it has a large and active community. Due to Python being one of the largest programming communities worldwide, PyTorch users benefit from shared knowledge, resources, and tools. There is extensive documentation and tutorials, and it is widely used in academia and industry.</li>
</ul>



<p>For a broader perspective on how PyTorch and TensorFlow differ, and when to choose each, check out <a href="https://blog.jetbrains.com/pycharm/2026/05/pytorch-vs-tensorflow-choosing-framework-2026/">this blog post</a>.</p>



<h3 class="wp-block-heading">Why use PyTorch for deep learning projects?</h3>



<p>PyTorch is at the core of the current deep learning ecosystem. In recent years, it has been the framework behind some of the most influential AI models, such as Meta’s Llama, OpenAI’s early GPT models, and Stable Diffusion. Today, it is a popular choice for AI research worldwide. </p>



<p>With a 63% adoption rate, PyTorch is the industry leader in model training, according to the Linux Foundation&#8217;s <a href="https://www.linuxfoundation.org/research/gen-ai-2024?__hstc=132719121.f1971c6f46a3ccdf486377c407d048b4.1731453421812.1738157614721.1738989160782.19&amp;__hssc=132719121.17.1738989160782&amp;__hsfp=3380644065" target="_blank" rel="noopener"><em>Shaping the Future Generative AI</em> report</a>. In academia, it is highly used in research paper implementations. It is preferred for research and development because of its intuitive design, which allows for easy experimentation and iteration. </p>



<p>Hence, researchers can develop novel architectures and test ideas simultaneously. <a href="https://leapcell.io/blog/tensorflow-vs-pytorch-a-comparative-analysis-for-2025" target="_blank" rel="noopener">PyTorch powers 85% of deep learning papers</a> presented at top AI conferences.&nbsp;</p>



<p>PyTorch is a framework of choice due to its advantages:</p>



<ul class="wp-block-list">
<li>Debugging with PyTorch is straightforward and natural since it runs as ordinary Python. Due to its dynamic graphing and real-time execution, developers can test and make changes to models using standard Python tools like print statements and debuggers – no special setups or workarounds are required. This sets PyTorch apart significantly from static-graph frameworks, where errors mostly emerge at runtime, and it can be challenging to trace them back to their source.</li>



<li>PyTorch is flexible due to its dynamic computation graph and intuitive API, so it is ideal for experimentation and rapid iteration.</li>



<li>PyTorch has a thriving community. According to the <a href="https://pytorch.org/blog/2024-year-in-review/" target="_blank" rel="noopener">PyTorch 2024 year in review</a>, there were contributions from more than 3,500 individuals and 3,000 organizations in a single year, and its tooling ecosystem grew by over 25%. The community has built up a huge library of tutorials, pre-trained models, and extensions. In particular, Hugging Face’s Transformers library, built directly on top of PyTorch, is now the standard toolkit for NLP research and development.</li>
</ul>



<h2 class="wp-block-heading">Understanding PyTorch tensors&nbsp;</h2>



<p>Understanding PyTorch requires an understanding of tensors. Every input, output, and model weight in PyTorch lives inside a tensor. Hence, tensors are not just a data format; they are the medium through which all computation flows. </p>



<p>Tensors are the core data structure in PyTorch. They are like n-dimensional arrays and matrices, but unlike regular arrays, tensors can be used on hardware accelerators like GPUs. Think of tensors as an extension of numbers we are already familiar with. A single number is a zero-dimensional tensor, a list of numbers is a one-dimensional tensor, and a table of numbers is a two-dimensional tensor. From there, you can add more dimensions to represent complex data like images, videos, or audio. </p>



<p>Neural networks accept tensors as input and generate tensors as output – even the parameters of a neural network, its weights and biases, are stored as tensors. For a visual explanation, you can watch a beginner-friendly video on tensors and deep learning:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="What Is TensorFlow? A Beginner-Friendly Introduction to Tensors and Deep Learning" src="https://www.youtube.com/embed/hm07b8ETaso?start=56&#038;feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<p>Tensors are similar to NumPy arrays but can also run on GPUs or other hardware accelerators. Often, tensors and NumPy arrays can share the same underlying memory, meaning that data doesn’t need to be copied. </p>



<p>The main difference is what happens when the calculation gets serious. NumPy is for scientific computing on a CPU. PyTorch tensors can be moved and processed on GPUs in one line of code, allowing for massive parallel computation and providing significant speedups for the types of matrix multiplication common in deep learning. </p>



<p>This enables the kind of processing that makes training large neural networks possible.</p>



<p>There are basic operations with PyTorch tensors that are essential. You can view the full implementation in this <a href="https://github.com/ashiorkornortey/pytorch-for-deeplearning-tutorial" target="_blank" rel="noopener">GitHub repository</a>.</p>



<p><strong>Creating a tensor</strong></p>



<p>The first thing you need to know is how to create a tensor. PyTorch gives you several ways depending on what your data looks like – you can build a tensor from an existing list, initialize one filled with zeros or ones as a placeholder, or generate one with random values as a starting point for a model&#8217;s weights.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import torch

# From a list
x = torch.tensor([1.0, 2.0, 3.0])

# Filled with zeros or ones
zeros = torch.zeros(3, 3)
ones = torch.ones(3, 3)

# Random values
rand = torch.rand(3, 3)

print(x)
print(zeros)
print(ones)
print(rand)</pre>



<p><em>This code snippet demonstrates different ways to create tensors in PyTorch. A tensor is created from a Python list, alongside tensors filled with zeros and ones, and a tensor containing randomly generated values. The output displays the resulting tensor structures and values, illustrating common methods used to initialize tensors for deep learning workflows.</em></p>



<p><strong>Basic arithmetic</strong></p>



<p>Tensor arithmetic works element-wise, meaning PyTorch applies the operation across every value in the tensor simultaneously rather than looping through one by one. This is what makes tensors so fast – and it is also what makes GPU acceleration so powerful, since GPUs are specifically designed to run thousands of these operations in parallel.&nbsp;</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">a = torch.tensor([1.0, 2.0, 3.0])
b = torch.tensor([4.0, 5.0, 6.0])

print(a + b)
print(a * b)
print(a.sum())
print(a.mean())</pre>



<p><em>This code snippet demonstrates common mathematical operations on PyTorch tensors. Two tensors are added and multiplied element-wise, while functions such as <code>sum()</code> and <code>mean()</code> are used to compute the total and average values of the tensor elements. The output displays the results of these operations, highlighting how PyTorch efficiently performs numerical computations on tensor data.</em></p>



<p><strong>Reshaping</strong></p>



<p>In deep learning, you will constantly need to reshape tensors – for example, flattening a 2D image into a 1D vector before passing it into a fully connected layer or reorganizing a batch of data to match what a model expects as input. PyTorch makes this straightforward with <code>reshape()</code>, which rearranges the data into a new shape without changing the underlying values.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">x = torch.ones(6)
x_reshaped = x.reshape(2, 3)
print(x_reshaped.shape)</pre>



<p><em>This code snippet demonstrates how to change the shape of a tensor using the reshape() function. A one-dimensional tensor of ones containing six elements is reshaped into a 2×3 tensor. The output shows the updated tensor structure, confirming that the data has been reorganized without altering its values.</em></p>



<p><strong>Moving to GPU</strong></p>



<p>By default, tensors are created on the CPU, but moving them to a GPU – where matrix operations can run orders of magnitude faster – takes just one line. This allows the same code to run on both GPU-equipped machines and machines that only have a CPU.&nbsp; It is good practice to check whether a GPU is available.&nbsp;</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">if torch.cuda.is_available():
   x = x.to("cuda")</pre>



<p><em>This code checks whether a CUDA-enabled GPU is available using <code>torch.cuda.is_available().</code> If a GPU is available, the tensor <code>x</code> is moved from the CPU to the GPU using <code>.to("cuda").</code> This enables faster computation by leveraging GPU acceleration, which is especially useful for large-scale deep learning tasks.&nbsp;</em></p>



<p><strong>Converting to and from NumPy</strong></p>



<p>PyTorch and NumPy use nearly the same language, so switching between them is simple. Chances are you are already using NumPy somewhere in your pipeline – for loading data, preprocessing, or visualizing results. </p>



<p>PyTorch is designed to work alongside it seamlessly. You can convert between tensors and NumPy arrays in one line, and on the CPU, they even share the same memory, so there is no performance cost to switching between them.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import numpy as np

# Tensor to NumPy
tensor = torch.tensor([1.0, 2.0, 3.0])
numpy_array = tensor.numpy()

print("Original PyTorch tensor:")
print(tensor)

print("\nConverted to NumPy array:")
print(numpy_array)

# NumPy to Tensor
numpy_array = np.array([1.0, 2.0, 3.0])
tensor = torch.from_numpy(numpy_array)

print("\nOriginal NumPy array:")
print(numpy_array)

print("\nConverted to PyTorch tensor:")
print(tensor)</pre>



<p><em>This snippet demonstrates interoperability between PyTorch and NumPy. A PyTorch tensor is first converted into a NumPy array using <code>.numpy()</code>, and then a NumPy array is converted back into a PyTorch tensor using <code>torch.from_numpy()</code>. The output shows that the values remain unchanged during the conversion process, highlighting seamless data sharing between the two libraries. This is particularly useful when integrating PyTorch models with NumPy-based preprocessing or analysis workflows.</em></p>



<h3 class="wp-block-heading">Setting up PyTorch&nbsp;</h3>



<p>PyCharm streamlines deep learning setup by integrating directly with Python environments and package management tools. One of its key strengths is its seamless integration with <a href="https://www.jetbrains.com/help/pycharm/jupyter-notebook-support.html" target="_blank" rel="noopener">Jupyter notebooks</a> and optional <a href="https://www.jetbrains.com/help/pycharm/google-colab-support.html" target="_blank" rel="noopener">Google Colab </a>support, allowing you to switch between local and cloud-based computation effortlessly.&nbsp;</p>



<p>Before creating the project, it is important to install <a href="https://docs.astral.sh/uv/getting-started/installation/#__tabbed_1_1" target="_blank" rel="noopener">uv</a>, a fast Python package and environment manager, locally. This enables PyCharm to create and manage project-specific environments using uv directly from the Python interpreter settings.</p>



<p>The setup process begins by creating a new project, where a project-specific Python environment is configured through the Python interpreter settings. During this step, a uv-managed environment and a Jupyter notebook are selected, too, enabling an interactive development environment from the beginning. </p>



<p>Version control can also be initialized using Git within this same window. For a detailed guide on creating and working with Jupyter notebooks in PyCharm, refer to the <a href="https://www.jetbrains.com/help/pycharm/editing-jupyter-notebook-files.html#create-jupyter-project" target="_blank" rel="noopener">PyCharm documentation</a>.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-1_welcome-to-pycharm.png" alt="" class="wp-image-726161"/><figcaption class="wp-element-caption"><em>From the PyCharm </em>Welcome<em> screen, click </em>New Project<em>. In the project configuration window, select </em>Jupyter <em>as the project type and choose </em>uv <em>as the environment manager under the Python interpreter settings. This creates a project-specific environment managed by uv and prepares the project for interactive deep learning development.</em></figcaption></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-2_this-is-a-sample-jupyter-notebook.png" alt="" class="wp-image-726172"/><figcaption class="wp-element-caption"><em>After the project is created, the selected Python interpreter is displayed in the bottom-right corner of the PyCharm window. The interpreter name should indicate that it is a uv-managed environment, confirming that the project is configured to use uv for package and environment management.</em></figcaption></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-3_sample-jupyter-notebook.png" alt="" class="wp-image-726183"/><figcaption class="wp-element-caption"><em>To install PyTorch using PyCharm&#8217;s graphical interface, open the package manager by navigating to </em>View |<em> </em>Tool Windows<em> | </em>Python Packages<em>. The </em>Python Packages<em> tool window provides a convenient way to search for, install, upgrade, and remove packages without using the terminal.</em></figcaption></figure>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-4_deep-learning-in-pytorch.png" alt="" class="wp-image-726194"/><figcaption class="wp-element-caption"><em>With the Python Packages tool window open, enter “torch” in the search bar to locate the PyTorch package. Select the package from the search results and click </em>Install<em>. The same process can be used to install related packages such as torchvision and torchaudio into the uv-managed project environment.</em></figcaption></figure>



<h4 class="wp-block-heading">Using Conda as an alternative</h4>



<p>If a Conda environment is preferred, PyCharm supports Conda directly through the Python interpreter settings. A Conda environment can be selected when setting up the project, and PyCharm will manage it automatically. Refer to the <a href="https://www.jetbrains.com/help/pycharm/conda-support-creating-conda-virtual-environment.html" target="_blank" rel="noopener">PyCharm documentation for Conda environments</a> for more details on configuring them.&nbsp;</p>



<p>Once the Conda environment is active, install PyTorch using the terminal:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia</pre>



<p>For PyTorch development, I recommend PyCharm because it provides excellent support for Python, intelligent coding assistance, debugging, version control, integrated database management, and seamless Docker integration. Specifically for <a href="https://www.jetbrains.com/pycharm/data-science/" target="_blank" rel="noopener">data science</a>, PyCharm supports Jupyter notebooks and <a href="https://www.jetbrains.com/help/pycharm/scientific-tools.html" target="_blank" rel="noopener">key scientific and machine learning libraries </a>and integrates with tools like the Hugging Face models library, Anaconda, and Databricks. </p>



<p>Additionally, it is particularly well-suited for PyTorch development because it understands the framework and includes features for layer-by-layer inspection of PyTorch tensors, which is essential when exploring data and building deep learning models. </p>



<p>Beyond tensors, PyCharm allows you to set breakpoints in training loops, inspect tensor values, and step through model forward passes using the integrated debugger – which works naturally with PyTorch’s dynamic computation graphs.&nbsp;&nbsp;</p>



<h2 class="wp-block-heading">Building neural networks with PyTorch&nbsp;</h2>



<p>A neural network is a system of connected layers that learns patterns from data by adjusting its internal weights through training. In PyTorch, all of these layers are contained within a single module called <code>torch.nn</code> . Think of it as your construction toolkit, which gives you everything you need to assemble a network without writing low-level mathematical operations from scratch.&nbsp;</p>



<p><code>torch.nn</code> comes with a library of predefined layers, such as <code>nn.Linear</code> for fully connected layers, <code>nn.Conv2d</code> for convolutional layers, and <code>nn.LSTM</code> for recurrent layers. Hence, you can focus on designing your network rather than implementing the math behind each layer. It provides all the building blocks needed to build your own neural network. </p>



<p>Every module in PyTorch subclasses <code>nn.Module</code>. As a neural network is itself a module that consists of other modules (layers), this nested structure allows for easily building and managing complex architectures.&nbsp;</p>



<p>When you build a neural network in PyTorch, you create a Python class that inherits from <code>nn.Module</code> and implements two core methods:</p>



<ul class="wp-block-list">
<li><code>__init__() –</code> where you define your layers.</li>



<li><code>forward() – </code>where you define how data flows through those layers.</li>
</ul>



<p>PyTorch&#8217;s autograd system automatically builds the computation graph based on the operations performed in the forward method, enabling automatic differentiation. The backward method, which handles gradient computation, typically does not need to be implemented manually. That means PyTorch handles the math behind gradient calculations, so you can focus on building.</p>



<p>Model building involves more than understanding the code. There are practicalities that need to be considered.</p>



<ul class="wp-block-list">
<li><strong>Constant iteration. </strong>There is a very high probability that your first model will not perform well. That is normal because deep learning is an experimental process that involves adjusting layers, activation functions, and hyperparameters until the model improves.</li>



<li><strong>Simplicity first, then complexity.</strong> A two-layer feedforward network is always a good starting point. It is advisable to add complexity, such as additional layers and different architectures, when it is clear that the simple model is insufficient.</li>
</ul>



<p>PyCharm makes model building easier thanks to its <a href="https://www.jetbrains.com/help/pycharm/debugging-code.html" target="_blank" rel="noopener">integrated debugger</a>. You can set breakpoints inside your forward method, inspect tensor values at each layer, and add step-throughs of your model pass by pass, which drastically reduces the time it takes to identify and fix problems.</p>



<h2 class="wp-block-heading">Build your first PyTorch handwritten digit classifier</h2>



<p>In this section, you will build a simple neural network in PyTorch that can recognize handwritten digits from the MNIST dataset. You will work through the complete workflow, starting from raw image data; you will prepare and normalize the dataset, define a neural network, train it to recognize digits, and evaluate how well it performs on test data. </p>



<p>Along the way, you will explore key deep learning concepts such as tensors, layers, activation functions, loss functions, optimization, and training loops, while using PyCharm to inspect and understand what happens inside the training loop.</p>



<p>In deep learning, image classification is a foundational task, in which a model learns to assign a label to an image based on its visual content. In this example, we’ll use image classification on the MNIST database of handwritten digits, a classic benchmark in computer vision that consists of 28 x 28 grayscale images of handwritten digits from 0 to 9. </p>



<p>It is small and well-structured, and using it as an example gives us the opportunity to focus on understanding the core building blocks of deep learning. The aim is to build a neural network using PyTorch that can accurately recognize and classify these digits.</p>



<p>The complete source code for this project is available in the accompanying <a href="https://github.com/ashiorkornortey/pytorch-for-deeplearning-tutorial" target="_blank" rel="noopener">GitHub repository</a>.</p>



<figure class="wp-block-image size-full"><a href="https://en.wikipedia.org/wiki/MNIST_database#/media/File:MNIST_dataset_example.png" target="_blank" rel="noopener"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-5_pytorch-tutorial.png" alt="" class="wp-image-726209"/></a><figcaption class="wp-element-caption"><em>MNIST dataset (</em><a href="https://en.wikipedia.org/wiki/MNIST_database#/media/File:MNIST_dataset_example.png" target="_blank" rel="noopener">source</a>)</figcaption></figure>



<h3 class="wp-block-heading">Preparing the data</h3>



<p>Before training any model, the data needs to be loaded, cleaned, and formatted so PyTorch can work with it efficiently. PyTorch provides two classes that handle this:</p>



<ul class="wp-block-list">
<li><strong><code>Dataset</code> </strong>defines how individual samples are accessed and returned.</li>



<li><strong><code>DataLoader</code> </strong>takes a <code>Dataset</code> and handles how data is fed into the model during training, including batching, shuffling, and parallel loading.</li>
</ul>



<p>As these components are configured, PyCharm helps streamline development through features such as code completion, automatic import suggestions, parameter hints, and quick documentation. </p>



<p>Hovering over PyTorch classes and functions shows usage information, and pressing <em>Ctrl+Q</em> opens detailed documentation directly within the IDE. Hence, it is easier to explore PyTorch APIs and correctly configure data loading and preprocessing steps without frequently switching to external documentation.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-6_pytorch-tutorial.png" alt="" class="wp-image-726220"/><figcaption class="wp-element-caption"><em>As <code>transforms.Normalize()</code> is typed, PyCharm displays the function signature and parameter information directly in the editor, helping developers configure data preprocessing steps more efficiently without referring to external documentation.&nbsp;</em></figcaption></figure>



<h3 class="wp-block-heading">Loading and normalizing the data</h3>



<p>Before training a neural network, the input data needs to be normalized so that the pixel values are scaled into a consistent range. This helps improve stability by keeping input values centered around zero and ensuring that gradients behave more predictably during optimization.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Download and load the training data
train_data = datasets.MNIST(
   root='./data',
   train=True,
   download=True,
   transform=transform
)</pre>



<p><em>The code snippet above downloads the MNIST dataset (if needed), loads the training images, and applies preprocessing so that the data is ready to be used in a neural network.</em></p>



<p>In this project, MNIST images are normalized as part of a preprocessing pipeline using PyTorch transforms:</p>



<ul class="wp-block-list">
<li><code>transforms.ToTensor()</code> converts images from pixel values (0–255) into floating-point tensors scaled to 0–1.&nbsp;</li>



<li><code>transforms.Normalize((0.5,), (0.5,))</code> then rescales these values to approximately -1 to 1, which helps stabilize training by keeping input values centered around zero and improving gradient behavior during optimization.&nbsp;</li>
</ul>



<p>PyTorch also provides key data-loading parameters to control how training data is processed:</p>



<ul class="wp-block-list">
<li><code>batch_size=64</code> means the model processes 64 images at a time instead of the full dataset. This improves memory efficiency and makes training more stable by allowing gradient updates on mini-groups of data rather than individual samples or the entire dataset.&nbsp;</li>



<li><code>shuffle=True</code> randomizes the order of images each epoch, so the model does not memorize the sequence.</li>



<li><code>download=True</code> means PyTorch fetches MNIST automatically on the first run, so you do not need to download anything manually.</li>
</ul>



<h3 class="wp-block-heading">Defining the model</h3>



<p>After the data is ready the next step is to build the neural network that will learn from it. The goal of the model is to take an input image of a handwritten digit and predict which digit (0–9) it represents. Each MNIST image is 28×28 pixels. Since the model cannot directly interpret images the way humans do, we first flatten each image into a single vector of 784 values (28 x 28 = 784). This converts the 2D image into a format the model can process.&nbsp;</p>



<p>The input layer takes the 784 pixel values and passes them through fully connected layers. Each layer learns weighted combinations of features that become increasingly useful for distinguishing digits. While these representations are not explicitly interpretable, the network gradually learns patterns that help separate different classes.&nbsp;</p>



<p>To help the model learn effectively, we use an activation function called ReLU, which allows the network to capture non-linear patterns that are essential for understanding images.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">class SimpleNetwork(nn.Module):
   def __init__(self):
       super(SimpleNetwork, self).__init__()
       self.fc1 = nn.Linear(784, 128)  # 28x28 = 784 input pixels
       self.fc2 = nn.Linear(128, 64)   # hidden layer
       self.fc3 = nn.Linear(64, 10)    # 10 outputs (digits 0-9)


   def forward(self, x):
       x = x.view(-1, 784)             # flatten the image
       x = F.relu(self.fc1(x))
       x = F.relu(self.fc2(x))
       x = self.fc3(x)
       return x


model = SimpleNetwork()
print(model)</pre>



<p>When you run the code, PyTorch prints the structure of the model:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">SimpleNetwork(
  (fc1): Linear(in_features=784, out_features=128, bias=True)
  (fc2): Linear(in_features=128, out_features=64, bias=True)
  (fc3): Linear(in_features=64, out_features=10, bias=True)
)</pre>



<p>The output shows the structure of the neural network. Each <code>Linear</code> layer represents a fully connected layer in the model. The first layer transforms the 784 input pixels into 128 features, the second reduces them to 64 features, and the final layer outputs 10 values representing the digit classes (0–9). This confirms that the model has been correctly defined before training begins.&nbsp;</p>



<h4 class="wp-block-heading">Using the Jupyter console to inspect data and validate the neural network</h4>



<p>One of the features that makes <a href="https://www.jetbrains.com/pycharm/editions/" target="_blank" rel="noopener">PyCharm Pro</a> especially useful for PyTorch development is the integrated Jupyter console. It connects directly to the running notebook kernel, allowing you to inspect tensors, explore datasets, test model outputs, and debug code interactively without adding temporary cells to the notebook. This streamlines the iterative workflow and makes it easier to validate code during model development.&nbsp;</p>



<p>To access the Jupyter console, first ensure that your Jupyter notebook is running. Then click <em>Open Jupyter Console</em> in the notebook toolbar at the top of the editor.</p>



<p>Additionally, PyCharm provides a <em>Variables</em> view that displays all active objects in the notebook kernel, allowing quick visual inspection of shapes, values, and types, and reducing the need for repeated print statements.&nbsp;</p>



<p>Together, these tools make it easier to inspect data and validate model behavior before training.</p>



<figure class="wp-block-image size-full"><img style="width:100% !important; height:auto !important; max-width:100% !important;" loading="lazy" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/image-7_mnist-classifier.png" alt="" class="wp-image-726234"/><figcaption class="wp-element-caption"><em>The Jupyter console allows the interactive execution of code linked to the notebook kernel, so you can inspect data and test the model before training. The </em>Variables <em>view displays active objects for quick inspection without print statements.</em></figcaption></figure>



<h3 class="wp-block-heading">Training the model</h3>



<h4 class="wp-block-heading">Choosing a loss function and optimizer</h4>



<p>Now that the model is defined, the next step is to train it so it can learn to recognize handwritten digits. During training, the model processes MNIST images, makes predictions, compares them to correct labels, and gradually improves its performance. To do this, we first need two key components: a loss function and an optimizer.</p>



<p>The loss function measures how far the model’s predictions are from the correct answers. In classification problems like MNIST (which has 10 classes, one for each digit), <code>CrossEntropyLoss</code> is used because it is designed for multi-class classification, and it not only penalizes incorrect predictions but also takes into account how confident the model is when it makes a mistake.</p>



<p>The optimizer is responsible for updating the model&#8217;s weights based on the loss. It determines how the model learns from its errors.&nbsp;</p>



<p>We also need to select an optimizer. Adaptive moment estimation (ADAM) and stochastic gradient descent (SGD) are two examples of these – they take the loss and adjust the model&#8217;s weights to do better next time. </p>



<p>The difference is how they do it. SGD updates model weights using a fixed learning rate applied to the computed gradients. ADAM extends this idea by adapting the learning rate for each parameter using estimates of past gradients, which often leads to faster and more stable convergence with less manual tuning. For this project, ADAM is the practical choice, with lr=0.001 as a safe default learning rate. SGD is worth exploring later when you want more control over the training process.</p>



<h4 class="wp-block-heading">Implementing a training loop</h4>



<p>The training loop is the core of the learning process. Each full pass through the training data is called an epoch. Training typically runs for multiple epochs so that the model can gradually improve its performance over time. </p>



<p>Each epoch is made up of smaller units called batches. Instead of processing the entire dataset at once, the model processes one batch at a time, which makes training more efficient and memory-friendly.</p>



<p>During each epoch, the model processes data in batches and repeats the same steps:</p>



<ul class="wp-block-list">
<li><strong>Forward pass:</strong> The model makes predictions (logits).</li>



<li><strong>Loss computation:</strong> The model compares predictions with true labels.</li>



<li><strong>Backward pass:</strong> The model computes gradients of the loss.</li>



<li><strong>Weight update:</strong> The optimizer adjusts model parameters.</li>
</ul>



<p>There are a few important implementation details to note when it comes to this section:</p>



<ul class="wp-block-list">
<li><code>model.train()</code> switches the model into training mode and must be called at the start of each epoch.</li>



<li><code>optimizer.zero_grad()</code> must be called before <code>loss.backward()</code> every iteration because, without it, PyTorch accumulates gradients from previous batches, which corrupts the updates. This is one of the most common beginner mistakes in PyTorch.</li>



<li><code>loss.item()</code> converts the loss tensor into a Python number for logging. This detaches it from the computation graph, ensuring it is not tracked for gradients.</li>
</ul>



<p>The code below implements the training loop and prints the loss at the end of each epoch:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Define loss function and optimizer
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# Training loop
epochs = 5

for epoch in range(epochs):
   model.train()
   running_loss = 0

   for images, labels in train_loader:
       # Forward pass
       predictions = model(images)
       loss = loss_fn(predictions, labels)

       # Backward pass
       optimizer.zero_grad()
       loss.backward()
       optimizer.step()

       running_loss += loss.item()

   avg_loss = running_loss / len(train_loader)
   print(f"Epoch {epoch+1}/5 — Loss: {avg_loss:.4f}")</pre>



<p>The output below shows the model’s training progress over five epochs, with the loss steadily decreasing as learning improves.&nbsp;</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Epoch 1/5 — Loss: 0.4014
Epoch 2/5 — Loss: 0.1937
Epoch 3/5 — Loss: 0.1364
Epoch 4/5 — Loss: 0.1116
Epoch 5/5 — Loss: 0.0957</pre>



<h4 class="wp-block-heading">Debugging the training process using the PyCharm debugger</h4>



<p>While <a href="https://www.jetbrains.com/help/pycharm/debugging-python-code.html" target="_blank" rel="noopener">basic Python debugging</a> is available in PyCharm, the PyCharm Pro subscription extends this capability by providing full support for <a href="https://www.jetbrains.com/help/pycharm/running-jupyter-notebook-cells.html#debug-code-in-jupyter-notebooks" target="_blank" rel="noopener">debugging Jupyter notebooks</a> and interactive machine learning workflows.</p>



<p>During model training, breakpoints can be set inside key stages of the training loop, such as the forward pass, allowing execution to pause while the notebook remains interactive. For the MNIST handwritten digit classification project developed in this tutorial, the breakpoint was placed on: <code>predictions = model(images)</code>. </p>



<p>This marks the start of the forward pass, in which a batch of input images is passed through the neural network to generate predictions. Pausing execution immediately before this line makes it possible to inspect the input data before the model processes it and then examine the model’s outputs after stepping over the line. This provides a clear view of how data flows through the network during training.</p>



<p>In the PyCharm debugger, the <a href="https://www.jetbrains.com/help/pycharm/debug-tool-window-watches.html#open-watches" target="_blank" rel="noopener"><em>Watches</em></a> pane lets you monitor custom expressions whenever execution pauses at a breakpoint. Rather than repeatedly evaluating expressions manually, watches automatically refresh their values after each debugging step, making it easier to inspect tensors and verify intermediate results throughout the training process.</p>



<p>For this project, the following watches were added:</p>



<ul class="wp-block-list">
<li><code>images.shape</code>, to verify the dimensions of each input batch.</li>



<li><code>labels.shape</code>, to confirm that the batch of labels corresponds to the input images.</li>



<li><code>predictions.shape</code>, to verify that the network produces an output tensor of the expected shape after the forward pass.</li>



<li><code>predictions.argmax(dim=1)[:5]</code>, to display the predicted digit for the first five images in the batch.</li>
</ul>



<p>After stepping over the forward pass, these watches automatically update to display the model’s outputs. This makes it straightforward to verify that the input tensors have the expected dimensions, confirm that the network produces a prediction for each image in the batch, and inspect the predicted digit classes without modifying the source code.&nbsp;</p>



<p>The debugging workflow described in this section is demonstrated in this video: </p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="Debugging in PyCharm" src="https://www.youtube.com/embed/s9BmqGf8Vdk?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div></figure>



<h3 class="wp-block-heading">Model evaluation</h3>



<p>Training is done, but a low training loss does not necessarily mean your model is good. It might have simply memorized the training data. Evaluation on unseen test data tells you how well it actually generalizes. After five epochs of training, the model achieves a test accuracy of 96.87%, correctly classifying 9,687 out of 10,000 previously unseen digits. </p>



<p>This indicates that the model generalizes well to new data for a simple fully connected architecture without additional optimization techniques. It also demonstrates one of PyTorch&#8217;s biggest strengths in practice: You can go from raw data to a working, accurate model with relatively little code.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">model.eval()
correct = 0
total = 0

with torch.no_grad():
   for images, labels in test_loader:
       predictions = model(images)
       _, predicted = torch.max(predictions, 1)
       total += labels.size(0)
       correct += (predicted == labels).sum().item()

accuracy = 100 * correct / total
print(f"Test Accuracy: {accuracy:.2f}%")</pre>



<h2 class="wp-block-heading">Advanced PyTorch techniques for deep learning&nbsp;</h2>



<p>There are advanced PyTorch techniques that can be explored when you grasp building and training basic models. They can take your work further, for example by allowing you to train faster, scale larger, or move a model into production. Some of them include:</p>



<ul class="wp-block-list">
<li><strong>GPU acceleration.</strong> One of the highest-impact changes you can make is moving your model and data to a GPU. Modern NVIDIA GPUs such as the A100, H100, and V100 are recommended to accelerate PyTorch with the greatest speedup. They offer exceptional performance, especially for features such as <code>torch.compile</code>.&nbsp;</li>



<li><strong>Distributed learning.</strong> When a single GPU is not enough – either because your model is too large or your dataset too vast – PyTorch&#8217;s <code>torch.distributed</code> backend lets you scale training across multiple GPUs or machines. <code>DistributedDataParallel</code> (DDP) enables distributed training across multiple GPUs or machines, significantly boosting compute power and reducing training time. When the capacity of a single GPU is exceeded by your model, DDP becomes essential and requires only a few additional lines of code to set up.</li>



<li><strong>Model deployment.</strong> Training a model is just one key aspect; eventually, you need to deploy it to real users. <a href="https://docs.pytorch.org/serve/" target="_blank" rel="noopener">TorchServe</a> is a flexible and easy-to-use tool for serving Python models in production. It supports deploying models in either eager or graph mode using TorchScript, serving multiple models concurrently, versioning models for A/B testing, loading and unloading models dynamically, and monitoring detailed logs and customizable metrics.&nbsp;</li>
</ul>



<p>These three techniques represent the natural progression of any advanced deep learning project. You start on a single machine, scale when needed, and ship when you are ready. They are worth exploring as your projects grow in ambition.</p>



<h2 class="wp-block-heading">Summary and resources</h2>



<p>In this tutorial, you went from understanding what PyTorch is to building and training a neural network that recognizes handwritten digits with over 96% accuracy. Also, we covered tensors, the <code>torch.nn</code> module, the training loop, and model evaluation, which are the core building blocks of every deep learning project built with PyTorch.</p>



<p>This is just the beginning. PyTorch&#8217;s real depth lies in what comes next – convolutional networks, transfer learning, and the vast Hugging Face ecosystem of pre-trained models, which run on a PyTorch backend, all built on the same foundations you learned here. Continue to experiment! Swap the optimizer, add a layer, and try a different dataset. </p>



<p>A great next step is to explore the <a href="https://docs.pytorch.org/tutorials/" target="_blank" rel="noopener">official PyTorch tutorials</a>, which cover everything from convolutional networks to deploying models in production. For a more structured learning path, the <em><a href="https://www.learnpytorch.io/" data-type="link" data-id="https://www.learnpytorch.io/" target="_blank" rel="noopener">Zero to Mastery</a></em> PyTorch course is free and beginner-friendly, picking up exactly where this tutorial ends.</p>



<h2 class="wp-block-heading">Build your first PyTorch model in PyCharm</h2>



<p>PyCharm gives you one environment for the full deep learning workflow: installing PyTorch, writing model code, running notebooks, debugging the training loop, inspecting tensors, tracking experiments, and managing your project with Git or Docker as it grows.</p>



<p><a href="https://www.jetbrains.com/pycharm/download/?section=windows" target="_blank" rel="noopener">Download PyCharm</a> for free and use this tutorial to build your first MNIST classifier.</p>



<p align="center"><a class="jb-download-button" title="Download PyCharm" href="https://jb.gg/auad4d" target="_blank" rel="noopener noreferrer" data-mce-href="https://jb.gg/auad4d" data-mce-selected="inline-boundary" data-mce-><i class="download-icon"></i>Download PyCharm</a></p>



<h2 class="wp-block-heading" id="author">About the author</h2>




    <div class="about-author ">
        <div class="about-author__box">
            <div class="row">
                                                            <div class="about-author__box-img">
                            <img style="width:100% !important; height:auto !important; max-width:100% !important;" decoding="async" src="https://blog.jetbrains.com/wp-content/uploads/2026/07/naa.jpeg" alt="" loading="lazy">
                        </div>
                                        <div class="about-author__box-text">
                                                    <h4>Naa Ashiorkor</h4>
                                                <p><span style="font-weight: 400;">Naa Ashiorkor is a data scientist and tech community builder. She is deeply involved in the Python community and serves as an organizer for various conferences, including EuroPython. She is currently building PyLadies Tampere.</span></p>
                    </div>
                            </div>
        </div>
    </div>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
