<?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>Neural Networks Archives - relataly.com</title>
	<atom:link href="https://www.relataly.com/category/machine-learning-algorithms/neural-networks/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.relataly.com/category/machine-learning-algorithms/neural-networks/</link>
	<description>The Business AI Blog</description>
	<lastBuildDate>Wed, 25 Jun 2025 08:08:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://www.relataly.com/wp-content/uploads/2023/04/cropped-AI-cat-Icon-White.png</url>
	<title>Neural Networks Archives - relataly.com</title>
	<link>https://www.relataly.com/category/machine-learning-algorithms/neural-networks/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">175977316</site>	<item>
		<title>Foundation Models Are Here: How Will They Impact Traditional Machine Learning?</title>
		<link>https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/</link>
					<comments>https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/#respond</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Sat, 25 Feb 2023 09:03:16 +0000</pubDate>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Generative AI]]></category>
		<category><![CDATA[Linear Regression]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Reinforcement Learning]]></category>
		<category><![CDATA[Classic Machine Learning]]></category>
		<category><![CDATA[Foundation Models]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=14189</guid>

					<description><![CDATA[<p>Just a year ago, ChatGPT was launched, and it has since catalyzed a seismic shift in the AI landscape. Given the astonishing capabilities of generative AI and the rapid evolution of foundation models, one question has risen to the forefront of discussions: What will be the impact of foundation models on classic machine learning and ... <a title="Foundation Models Are Here: How Will They Impact Traditional Machine Learning?" class="read-more" href="https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/" aria-label="Read more about Foundation Models Are Here: How Will They Impact Traditional Machine Learning?">Read more</a></p>
<p>The post <a href="https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/">Foundation Models Are Here: How Will They Impact Traditional Machine Learning?</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<p>Just a year ago, ChatGPT was launched, and it has since catalyzed a seismic shift in the AI landscape. Given the astonishing capabilities of generative AI and the rapid evolution of foundation models, one question has risen to the forefront of discussions: What will be the impact of foundation models on classic machine learning and so-called &#8220;narrow AI&#8221;?</p>



<p>The opinions on this topic are sharply divided. Some are convinced that foundation models are poised to completely overshadow traditional machine learning methods. Others remain steadfast in their belief that classic ML techniques have enduring value. As we&#8217;ll explore in this article, the reality is far more nuanced than a straightforward &#8220;yes&#8221; or &#8220;no.&#8221;</p>



<p>To dissect this complex issue, we&#8217;ll delve into a range of application domains, from classification and regression to natural language processing (NLP), clustering, and computer vision, among others. We&#8217;ll also discuss a few concepts and terms along the way. On top of that, we will examine additional considerations such as interpretability, computational demands, and reliability.</p>



<p>Having worked a few years classic ml and since the end of 2022 mostly on Generative AI, I would like to share a few thoughts. Please take them with some caution, as they are mostly based on personal experience in the field and my conversations with organizations on how they can benefit from generative AI. </p>



<h2 class="wp-block-heading">The Beginnings Classic Machine Learning</h2>



<p>When I started my career a few years ago in 2017 the world was an entirely different one. There were already some tools that would help with that but instead of Python, but most data scientists were still using R (yes, yes, its still being used). There was also no ChatGPT, and the closest thing available was BERT; which was introduced by Google in 2017 and technologially marked an important step towards todays foundation models.  </p>



<p>If you you wanted to use AI in 2017, you would typically need to build your own model for your specific use case. You had to collect the data, prepare it (still today takes most of the time), and then train a model, test it and deploy it into production. Oh and then monitor it. </p>



<p>This is the classic data sciense process and it is complex, with a lot of steps that in many companies took several month. And there is a catch to it. At the end of the process, when you have built your model, it is what we call today narrow AI. </p>



<p>Why Narrow? Because the model is only there for a specific use case. If you have a slightly different use case, you would typically need to train a new model. As a result, companies that were successful in building ML models, quickly end up with a large number of models they need to run and monitor, which adds additional complexity. </p>



<h2 class="wp-block-heading">Pretrained Models &#8211; Comodity AI</h2>



<p>There are certain AI applications that we just see again and again and that stay mostly the same. Many of these applications are in the area of computer vision and audio recognition. </p>



<p>An example, is face recognition. Cloud providers soon recognized that the models they often had built for their own purpose, could also be useful to their customers. Face recognition, voice recognition, or information extraction from invoices have become commidities. These models are relatively complex to built from scratch but have a high degree of standardization. So customers who want to use these models often decide to not build them themselves but buy them as a service from an external provider.</p>



<p>Yet, these models are still narrow AI because they can only do one thing. </p>



<h2 class="wp-block-heading">Foundation Models</h2>



<p>With ChatGPT, LLMA2, Bard, etc. we are embarking from the world of narrow AI and enter the realm of foundation models. Its a paradigm shift not only in the way we create AI models but also in the way we interact with them. </p>



<p>Foundation models are fundamentally different from classic ML in the way they are trained. Instead of using the data from a specific problem, and training a model, foundation models are trained on large amounts of natural language &#8211; a major part of what is available today in the public internet in different languages, incl. wikipedia, computer code, etc. The training process takes up to several month, costs millions of dollar and results in extremely capable models. </p>



<p>Foundation models built on several technologies that were developed throughout the years. A few examples:</p>



<ul class="wp-block-list">
<li>Machine learning: Instead of explicitly programming foundation models, they learn from the provided data and identify patterns.</li>



<li>Transformers: Were a milestone and part of the BERT model. It introduces an effective way of training word prediction models with a high degree of parallelization. </li>



<li>Supervised learning: foundation models are trained on labeled datasets.</li>



<li>To further improve the models, foundation models are trained using reinformcenent learning with human feedback. </li>



<li>Deep learning: Foundation models use large neural networks with a large number of layers and neurons. </li>
</ul>



<p>The sheer size of these models make them possible to solve a whole lot of tasks. However, there are things that these models were so far struggling with, for example, solving math problems, halluzinations, etc.And regardless of the task you communicate and instruct foundation models via a prompt. </p>



<h2 class="wp-block-heading">What can Foundation Models Do Compared to Classic ML?</h2>



<p>Foundation models and classic machine learning (ML) techniques embody different approaches to problem-solving in the realm of artificial intelligence, each with its distinct advantages, disadvantages, and ideal use-cases. Here’s a nuanced comparison across various application domains and aspects.</p>



<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="1855" height="871" data-attachment-id="14217" data-permalink="https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/image-31/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/10/image.png" data-orig-size="1855,871" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/10/image.png" src="https://www.relataly.com/wp-content/uploads/2023/10/image.png" alt="" class="wp-image-14217" style="aspect-ratio:2.1333333333333333;width:974px;height:auto" srcset="https://www.relataly.com/wp-content/uploads/2023/10/image.png 1855w, https://www.relataly.com/wp-content/uploads/2023/10/image.png 300w, https://www.relataly.com/wp-content/uploads/2023/10/image.png 512w, https://www.relataly.com/wp-content/uploads/2023/10/image.png 768w, https://www.relataly.com/wp-content/uploads/2023/10/image.png 1536w" sizes="(max-width: 1237px) 100vw, 1237px" /></figure>



<p>The classic ML field is still in several application domains that have emerged over time. These areas can be roughly differentiated into:</p>



<ul class="wp-block-list">
<li>classification (of structured inputs; two and multiclass)</li>



<li>regression &amp; time series forecasting</li>



<li>clustering</li>



<li>outlier detection</li>



<li>recommender systems</li>



<li>natural language processing (NLP)</li>



<li>computer vision</li>



<li>audio processing</li>
</ul>



<p>I belive these categories cover at least 90% of the use cases in the area of machine learning. Next let&#8217;s take a closer look at these categories. I will be mostly focusing on performance and discuss other aspects at the end of this article.</p>
</div>
</div>



<h2 class="wp-block-heading">#1 Classification (on Tabular Datasets)</h2>



<p>In the domain of classification, classic ML approaches like logistic regression, decision trees, and SVMs have been historically employed. If you have been following my blog then you know i have also explained a few of their applications (<a href="https://www.relataly.com/predicting-the-purchase-intention-of-online-shoppers/982/">Classifying Purchase Intention of Online Shoppers with Python</a> etc.). </p>



<h3 class="wp-block-heading">Performance</h3>



<p>These classic algorithms work well when the data is structured and the relationship between the input and the output is relatively straightforward. A classic example is the titantic dataset where the goal is to predict surval of passengers based on age, gender, cabin, etc. Foundation models, due to their ability to learn from vast amounts of data, not only take into account the structured variablesbut potetially also make sense of additional information such as names etc., that would otherwise be more difficult to use. In other cases, LLMs could also make sense of text snippets and extract additional information.</p>



<p>LLMs perform quite well on simpler classification tasks, but for more complex ones with many categories or high-dimensional data, custom trained and hyperparameter-tuned models outperform them. However, the potential in traditional algorithm for further improvements is rather low. On the other hand, foundation models are likely to improve further in the coming year and will thus likely see increasing adoption for more complex classification tasks. </p>



<h3 class="wp-block-heading">Limitations</h3>



<p>Indeed, LLMs offer unparalleled flexibility in this domain, especially with text-based tasks. Their ability to generalize over different types of data makes them highly versatile. Their auto-adjustment to data drift can save costs on retraining, a significant benefit. </p>



<p>For complex cases, the bgiggest limitation is the token limit. Even for LLM finetuning, you will have a hard time training the model with a large number (several hundrets) of input variables. As long as the token limit does not grow considerably, classification tasks are limited to cases with medium complexity. With better fine-tuning options and longer token limits, the use of foundation models will likely grow. </p>



<h3 class="wp-block-heading">Outlook</h3>



<p>Classic ML and foundation models will coexist, with foundation models gaining more traction as they overcome current limitations like token limits.</p>



<h2 class="wp-block-heading">#2 Regression and Time Series Forecasting</h2>



<p>Methods like <strong>Linear Regression</strong>, <strong>ARIMA</strong> (AutoRegressive Integrated Moving Average), and <strong>Prophet</strong> have long been the go-to choices for both regression and time series analysis. Their popularity stems from there interpretability and easy of implementation. Foundation models are not great with mathematics but can create solid forecasts. However, they lack the interpretability and reliability that is often key for forecasting use cases. In addition, for longer time series, the limited context window becomes a problem again, especially as floadting numbers take a lot of tokens. On the other hand, foundation models may be able to identify more complex patterns that traditional techniques would likely oversee.</p>



<p>Also: <a href="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/">Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python</a></p>



<p>Outlook: At the moment, regression tasks are a domain where classic machine learning has an edge over foundation models. It remainst o be seen how long this will last.</p>



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



<p>NLP comprises a variety of disciplines from recognition, text completion, text classification and information extraction, reasoning over text. But with the rise of BERT and ROBERTA this bastion of data science begun to crumble. Now with modern LLMs, it seems entirely lost to foundation models. </p>



<p>In all of these fields, LLMs like GPT-4 show superior performance. The large-scale training and vast data encompassed by foundation models make them highly efficient at generation tasks, often surpassing custom models. GPT-4 and ChatGPT were both able to <a href="https://www.businessinsider.com/list-here-are-the-exams-chatgpt-has-passed-so-far-2023-1?r=US&amp;IR=T">pass numerous exams inlcuding the Bar exam</a>. </p>



<p>Outlook: Foundation models are already ahead and likely to take the rest of the entire share. </p>



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



<p>As LLMs generate meaningful embeddings, they indeed offer exciting prospects for clustering tasks. Traditional clustering algorithms might still be useful for specific use cases or where interpretability is crucial.</p>



<p>Embeddings work well for clustering techniques. Its the same technique that is based on the distance between objects and works both with numeric inputs as well as with text in different languages. </p>



<p>However, classic ml algorithms were always struggling with more complex patterns in data and as of now I have reason to believe that the situation is similar with LLMs. However, this assessment is only for the traditional LLMs but not for vision models. GPT-4 V was just released a few weeks and its image interpretation capabilities are crazy good. This model will also be able to interpret outliers and identify unusual geometric shapes in cluster diagrams that would be hard to detect with traditional models. Its a game changer for outlier detection and clastering alike.  </p>



<p>Outlook: Foundation models taking an increasing spot </p>



<h2 class="wp-block-heading">Outlier Detection</h2>



<p>While outlier detection is performed differently from clustering in classic ml, foundation models perform a similar technique both for clustering and outlier detection.</p>



<p>While LLMs are good at detecting patterns, traditional ML has established algorithms for outlier detection that work exceptionally well in controlled settings, especially when we have a clear understanding of the data&#8217;s distribution.</p>



<p>When we are talking about outlier detection, classic ML models will give you a more fine-grained control over what is considered an outlier and here classic ml I believe will stay relevant for </p>



<p>Outlook: Classic ML models remain relevant, although foundation models will be used for certain tasks</p>



<h2 class="wp-block-heading">Computer Vision</h2>



<p>Your differentiation is spot on. While foundation models have made strides in general computer vision tasks, specialized tasks like medical imaging or self-driving cars require models built and trained for that specific purpose.</p>



<p>Outlook: The future will likely have a coexistence between custom trained special models for specific tasks like autononoums systems, and simpler more generic tasks like face detection, object detection and tagging. At the same time, foundation models have added a few new disciplines like image interpreation and image generation, where custom models have never been able to produce good results.</p>



<p>For specific task like getting exact coordinates, computer vision models will have certain limitations. Here traditional models and pretrained models will have an edge.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="512" height="230" data-attachment-id="14193" data-permalink="https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/image-28/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/08/image.png" data-orig-size="1999,897" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/08/image.png" src="https://www.relataly.com/wp-content/uploads/2023/08/image-512x230.png" alt="" class="wp-image-14193" srcset="https://www.relataly.com/wp-content/uploads/2023/08/image.png 512w, https://www.relataly.com/wp-content/uploads/2023/08/image.png 300w, https://www.relataly.com/wp-content/uploads/2023/08/image.png 768w, https://www.relataly.com/wp-content/uploads/2023/08/image.png 1536w, https://www.relataly.com/wp-content/uploads/2023/08/image.png 1999w" sizes="(max-width: 512px) 100vw, 512px" /></figure>



<p>Outlook: Foundation models taking a large share of computer vision tasks. </p>



<h2 class="wp-block-heading">Audio Processing</h2>



<p>As foundation models expand their capabilities, they&#8217;re likely to dominate tasks like transcription or synthesis. However, specialized tasks such as voice biometrics or unique sound classifications might still rely on custom models.</p>



<p>Outlook: Foundation models taking the share entirely</p>



<h2 class="wp-block-heading">Recommender Systems</h2>



<p>Traditional recommendation systems, especially those utilizing collaborative filtering or matrix factorization, have been honed over years and work remarkably well. LLMs can augment these systems by providing context-rich content recommendations or by understanding nuanced user preferences.</p>



<p>In summary, while LLMs have certainly expanded the frontiers of what&#8217;s possible, especially in the realm of NLP, they won&#8217;t replace traditional ML/DL across the board. Instead, they complement existing systems, offering new capabilities and efficiencies. The choice between the two often boils down to the specifics of the task, the available data, and the desired outcomes. The future likely holds a symbiotic relationship where both coexist, each playing to its strengths.</p>



<p>Outlook: I am unsure. I could imagine that foundation are increasingly be used in combination with classic models.</p>



<h3 class="wp-block-heading">Additional Aspects:</h3>



<ol class="wp-block-list">
<li><strong>Interpretability</strong>:
<ul class="wp-block-list">
<li><em>Classic ML</em>: Often more interpretable, which is crucial for understanding model decisions in sensitive domains.</li>



<li><em>Foundation Models</em>: The “black box” nature makes them less interpretable, although efforts like LIME or SHAP are attempting to mitigate this issue.</li>
</ul>
</li>



<li>Consistency and reliability:
<ul class="wp-block-list">
<li>Classic ML results are still more predictable. In cases of high stakes, classic ML will be used more than generative AI.</li>
</ul>
</li>



<li><strong>Deployment and Maintenance</strong>:
<ul class="wp-block-list">
<li><em>Classic ML</em>: Easier to deploy and maintain due to their simplicity and lower resource requirements.</li>



<li><em>Foundation Models</em>: Can be resource-intensive to deploy and maintain, especially at the scale necessary for real-world applications.</li>
</ul>
</li>



<li><strong>Customization</strong>:
<ul class="wp-block-list">
<li><em>Classic ML</em>: Certain well-understood problems have commoditized solutions available.</li>



<li><em>Foundation Models</em>: Pushing the boundary of commoditization further by providing powerful, generalized solutions that can be fine-tuned for specific tasks.</li>
</ul>
</li>
</ol>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>Not long ago, if somebody would have asked me, if these models could entirely replace classic machine learning, i would have said, of course not, they will still have a place. There were just too many fields, where training a custom machine learning model makes just more sense. Computer vision is a great example, as soon as you want to count specific objects, you would typeically not come around training a custom model for that. Of course you would use existing libraries and maybe pretrained models, but still you would have to do a main part of the job. </p>



<p>Foundation models are undeniably transformative, especially in domains like NLP and Computer Vision, where they often outperform classical ML techniques. However, the necessity for interpretability, lower resource requirements, and specific problem-tailored solutions in certain scenarios ensures that classical ML will continue to hold its relevance. The evolution towards more capable foundation models doesn&#8217;t signify the end of classical ML, but rather presents an enriched tapestry of tools and methodologies that practitioners can draw upon to tackle complex problems in the AI landscape.</p>



<p>How does it look today? A few month later GPT-4 has been released, which is already much better at solcingf math problems than GPT3.5. Only a few weeks before, GPT-4 V has been released, which is extremely good at interperting images.</p>



<h2 class="wp-block-heading">Sources and Further Reading</h2>



<p><a href="https://arxiv.org/abs/2304.11633">[2304.11633] Evaluating ChatGPT&#8217;s Information Extraction Capabilities: An Assessment of Performance, Explainability, Calibration, and Faithfulness (arxiv.org)</a></p>



<p><a href="https://www.businessinsider.com/list-here-are-the-exams-chatgpt-has-passed-so-far-2023-1?r=US&amp;IR=T">List: Here Are the Exams ChatGPT and GPT-4 Have Passed so Far (businessinsider.com)</a></p>



<p></p>
<p>The post <a href="https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/">Foundation Models Are Here: How Will They Impact Traditional Machine Learning?</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/foundation-models-are-here-how-will-they-impact-traditional-machine-learning/14189/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">14189</post-id>	</item>
		<item>
		<title>Stock Market Forecasting Neural Networks for Multi-Output Regression in Python</title>
		<link>https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/</link>
					<comments>https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/#comments</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Tue, 13 Jul 2021 21:10:23 +0000</pubDate>
				<category><![CDATA[Finance]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Scikit-Learn]]></category>
		<category><![CDATA[Seaborn]]></category>
		<category><![CDATA[Stock Market Forecasting]]></category>
		<category><![CDATA[Time Series Forecasting]]></category>
		<category><![CDATA[Yahoo Finance API]]></category>
		<category><![CDATA[AI in Finance]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Intermediate Tutorials]]></category>
		<category><![CDATA[Multi-output Neural Network]]></category>
		<category><![CDATA[Multi-Step Time Series Forecasting]]></category>
		<category><![CDATA[Multivariate Models]]></category>
		<category><![CDATA[Stock Market Prediction]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=5800</guid>

					<description><![CDATA[<p>Multi-output time series regression can forecast several steps of a time series at once. The number of neurons in the final output layer determines how many steps the model can predict. Models with one output return single-step forecasts. Models with various outputs can return entire series of time steps and thus deliver a more detailed ... <a title="Stock Market Forecasting Neural Networks for Multi-Output Regression in Python" class="read-more" href="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/" aria-label="Read more about Stock Market Forecasting Neural Networks for Multi-Output Regression in Python">Read more</a></p>
<p>The post <a href="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/">Stock Market Forecasting Neural Networks for Multi-Output Regression in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Multi-output time series regression can forecast several steps of a time series at once. The number of neurons in the final output layer determines how many steps the model can predict. Models with one output return single-step forecasts. Models with various outputs can return entire series of time steps and thus deliver a more detailed projection of how a time series could develop in the future. This article is a hands-on Python tutorial that shows how to design a neural network architecture with multiple outputs. The goal is to create a multi-output model for stock-price forecasting using Python and Keras. By the end of this tutorial, you will have learned how to design a multi-output model for stock price forecasting using Python and Keras. This knowledge can be applied to other types of time series forecasting tasks, such as weather forecasting or sales forecasting.</p>



<p>This article proceeds as follows: We briefly discuss the architecture of a multi-output neural network. After familiarizing ourselves with the model architecture, we develop a Keras neural network for multi-output regression. For data preparation, we perform various steps, including cleaning, splitting, selecting, and scaling the data. Afterward, we define a model architecture with multiple LSTM layers and ten output neurons in the last layer. This architecture enables the model to generate projections for ten consecutive steps. After configuring the model architecture, we train the model with the historical daily prices of the Apple stock. Finally, we use this model to generate a ten-day forecast.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<div class="wp-block-kadence-infobox kt-info-box_317393-a1"><span class="kt-blocks-info-box-link-wrap info-box-link kt-blocks-info-box-media-align-top kt-info-halign-left"><div class="kt-infobox-textcontent"><h2 class="kt-blocks-info-box-title">Disclaimer</h2><p class="kt-blocks-info-box-text">This article does not constitute financial advice. Stock markets can be very volatile and are generally difficult to predict. Predictive models and other forms of analytics applied in this article only serve the purpose of illustrating machine learning use cases.</p></div></span></div>
</div></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<div style="height:5px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="h-multi-output-regression-vs-single-output-regression">Multi-Output Regression vs. Single-Output Regression</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In time series regression, we train a statistical model on the past values of a time series to make statements about how the time series develops further. During model training, we feed the model with so-called mini-batches and the corresponding target values. The model then creates forecasts for all input batches and compares these predictions to the actual target values to calculate the residuals (prediction errors). In this way, the model can adjust its parameters iteratively and learn to make better predictions.</p>



<p>Multivariate forecasting models take into account multiple input variables, such as historical time series data and additional features like moving averages or momentum indicators, to improve the accuracy of their predictions. The idea is that these various variables can help the model identify patterns in the data that suggest future price movements.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"><div class="wp-block-image">
<figure class="alignright size-large is-resized"><img decoding="async" data-attachment-id="7569" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/multi-output-neural-networks-time-series-regression-architecture/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png" data-orig-size="2017,1342" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="multi-output-neural-networks-time-series-regression-architecture" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png" src="https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture-1024x681.png" alt="An exemplary architecture of a neural network with five input neurons (blue) and four output neurons (red), keras, python, tutorial, stock market prediction" class="wp-image-7569" width="371" height="247" srcset="https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png 1024w, https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png 300w, https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png 768w, https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png 1536w, https://www.relataly.com/wp-content/uploads/2022/04/multi-output-neural-networks-time-series-regression-architecture.png 2017w" sizes="(max-width: 371px) 100vw, 371px" /><figcaption class="wp-element-caption">An exemplary architecture of a neural network with five input neurons (blue) and four output neurons (red)</figcaption></figure>
</div></div>
</div>



<h2 class="wp-block-heading">The Architecture of a Neural Network with Multiple Outputs</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Next, we will discuss the architecture of a neural network with multiple outputs. The architecture consists of several layers, including an input layer, several hidden layers, and an output layer. The number of neurons in the first layer must match the input data, and the number of neurons in the output layer determines the period length of the predictions. </p>



<p>Models with a single neuron in the output layer are used to predict a single time step. It is possible to predict multiple price steps with a single-output model. It requires a <a href="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/" target="_blank" rel="noreferrer noopener">rolling forecasting approach</a> in which the outputs are iteratively reused to make further-reaching predictions. However, this way is somewhat cumbersome. A more elegant way is to train a multi-output model right away.</p>



<figure class="wp-block-image size-large is-resized is-style-default"><img decoding="async" data-attachment-id="7586" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/architecture-neural-network-multi-output-regression-model/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png" data-orig-size="3395,1503" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="architecture-neural-network-multi-output-regression-model" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png" src="https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model-1024x453.png" alt="The inputs and outputs of a neural network for time series regression with five input neurons and four outputs. Stock market forecasting" class="wp-image-7586" width="755" height="334" srcset="https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 1024w, https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 300w, https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 768w, https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 1536w, https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 2048w, https://www.relataly.com/wp-content/uploads/2022/04/architecture-neural-network-multi-output-regression-model.png 2475w" sizes="(max-width: 755px) 100vw, 755px" /><figcaption class="wp-element-caption">The inputs and outputs of a neural network for time series regression with five input neurons and four outputs</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<p> </p>



<h2 class="wp-block-heading">Training Neural Networks with Multiple Outputs</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>A model with multiple neurons in the output layer can predict numerous steps once per batch. Multi-output regression models train on many sequences of subsequent values, followed by the consecutive output sequence. The model architecture thus contains multiple neurons in the initial layer and various neurons in the output layer (as illustrated). </p>



<p>In a multi-output regression model, each neuron in the output layer is responsible for predicting a different time step in the future. To train such a model, you need to provide a sequence of input data followed by the corresponding sequence of output data. For example, if you want to predict the stock price for the next ten days, you would provide a sequence of input data containing the historical stock prices for the past 50 days, followed by a sequence of output data containing the stock prices for the next 10 days.</p>



<p>The model will then learn to map the input sequence to the output sequence so that it can make predictions for multiple time steps in the future based on the input data. </p>



<p>In the next part of this tutorial, we will walk through the process of developing a multi-output regression model in more detail.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h2 class="wp-block-heading has-contrast-color has-text-color" id="h-implementing-a-neural-network-model-for-multi-output-multi-step-regression-in-python">Implementing a Neural Network Model for Multi-Output Multi-Step Regression in Python</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Let&#8217;s get started with the hands-on Python part. In the following, we will develop a neural network with Keras and Tensorflow that forecasts the Apple stock price. To prepare the data for a neural network with multiple outputs in time series forecasting, we will spend the most time preparing it and bringing it into the right shape. Broadly this involves the following steps:</p>



<ol class="wp-block-list">
<li>Load the time series data that we want to use as input and output for your model. We use historical price data that is available via the yahoo finance API.</li>



<li>Then we split our data into training and testing sets. We will use the training set to fit the model and the testing set to evaluate the model&#8217;s performance.</li>



<li>Preprocess the data: This includes scaling the data and selecting relevant features.</li>



<li>Reshape the data and bring them into a format that can be input into the neural network. This involves converting the data into a 3D array for time series data.</li>



<li>Finally, we will train our model and generate the forecasting.</li>
</ol>



<p>The code is available on the GitHub repository.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_e5fd46-d1"><a class="kb-button kt-button button kb-btn_25b4a6-dd kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/01%20Time%20Series%20Forecasting%20%26%20Regression/006%20Multi-Output%20Regression.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_3ee95e-9c kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly GitHub Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large"><img decoding="async" width="512" height="337" data-attachment-id="12797" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/multiple_waterfalls_coming_from_a_single_waterfall/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall.png" data-orig-size="768,505" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="multiple_waterfalls_coming_from_a_single_waterfall" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall.png" src="https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall-512x337.png" alt="Neural network architectures with multiple outputs allow for more potent solutions but are more complex to train. Image created with Midjourney. Stock market forecasting, multi-output multi-step  regression, python" class="wp-image-12797" srcset="https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall.png 512w, https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall.png 300w, https://www.relataly.com/wp-content/uploads/2023/03/multiple_waterfalls_coming_from_a_single_waterfall.png 768w" sizes="(max-width: 512px) 100vw, 512px" /><figcaption class="wp-element-caption">Neural network architectures with multiple outputs allow for more potent solutions but are more complex to train. Image created with <a href="http://www.midjourney.com" target="_blank" rel="noreferrer noopener">Midjourney</a>.</figcaption></figure>



<p></p>
</div>
</div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Before beginning the coding part, ensure that you have set up your Python 3 environment and required packages. If you don&#8217;t have a Python environment, consider <a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda</a>. To set it up, you can follow the steps in&nbsp;<a href="https://www.relataly.com/category/data-science/setup-anaconda-environment/" target="_blank" rel="noreferrer noopener">this tutorial</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>
</ul>



<p>In addition, we will be using the machine learning libraries Keras, Scikit-learn, and Tensorflow. For visualization, we will be using the Seaborn package.</p>



<p>Please also have either the <a href="https://pandas-datareader.readthedocs.io/en/latest/" target="_blank" rel="noreferrer noopener">pandas_datareader</a> or the <a href="https://pypi.org/project/yfinance/" target="_blank" rel="noreferrer noopener">yfinance</a> package installed. You will use one of these packages to retrieve the historical stock quotes.</p>



<p>You can install these packages using console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-step-1-load-the-data">Step #1: Load the Data</h3>



<p>The Pandas DataReader library is our first choice for interacting with the yahoo finance API. If the library causes a problem (it sometimes does), you can also use the yfinance package, which should return the same data. We begin by loading historical price quotes of the Apple stock from the public yahoo finance API. Running the code below will load the data into a Pandas DataFrame.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># import pandas_datareader as webreader # Remote data access for pandas
import math # Mathematical functions 
import numpy as np # Fundamental package for scientific computing with Python
import pandas as pd # Additional functions for analysing and manipulating data
from datetime import date, timedelta, datetime # Date Functions
from pandas.plotting import register_matplotlib_converters # This function adds plotting functions for calender dates
import matplotlib.pyplot as plt # Important package for visualization - we use this to plot the market data
import matplotlib.dates as mdates # Formatting dates
from sklearn.metrics import mean_absolute_error, mean_squared_error # Packages for measuring model performance / errors
from keras.models import Sequential # Deep learning library, used for neural networks
from keras.layers import LSTM, Dense, Dropout # Deep learning classes for recurrent and regular densely-connected layers
from keras.callbacks import EarlyStopping # EarlyStopping during model training
from sklearn.preprocessing import RobustScaler, MinMaxScaler # This Scaler removes the median and scales the data according to the quantile range to normalize the price data 
import seaborn as sns

# from pandas_datareader.nasdaq_trader import get_nasdaq_symbols
# symbols = get_nasdaq_symbols()

# Setting the timeframe for the data extraction
today = date.today()
date_today = today.strftime(&quot;%Y-%m-%d&quot;)
date_start = '2010-01-01'

# Getting NASDAQ quotes
stockname = 'Apple'
symbol = 'AAPL'
# df = webreader.DataReader(
#     symbol, start=date_start, end=date_today, data_source=&quot;yahoo&quot;
# )

import yfinance as yf #Alternative package if webreader does not work: pip install yfinance
df = yf.download(symbol, start=date_start, end=date_today)

# # Create a quick overview of the dataset
df.head()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Tensorflow Version: 2.6.0
Num GPUs: 1
[*********************100%***********************]  1 of 1 completed
			Open		High		Low			Close		Adj Close	Volume
Date						
2010-01-04	7.622500	7.660714	7.585000	7.643214	6.515213	493729600
2010-01-05	7.664286	7.699643	7.616071	7.656429	6.526477	601904800
2010-01-06	7.656429	7.686786	7.526786	7.534643	6.422666	552160000
2010-01-07	7.562500	7.571429	7.466071	7.520714	6.410791	477131200
2010-01-08	7.510714	7.571429	7.466429	7.570714	6.453413	447610800</pre></div>



<p>The data should comprise the following columns:</p>



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



<li>Open</li>



<li>High</li>



<li>Low</li>



<li>Adj Close</li>



<li>Volume</li>
</ul>



<p>The target variable that we are trying to predict is the Closing price (Close).</p>



<h3 class="wp-block-heading">Step #2: Explore the Data</h3>



<p>Once we have loaded the data, we print a quick overview of the time-series data using different line graphs. The following code will plot a line chart for each column in df_plot using the <code>seaborn</code> library. The charts will be organized in a grid with nrows number of rows and ncols number of columns. The sharex parameter is set to True, which means that the x-axes of the subplots will be shared. The figsize parameter determines the size of the plot in inches.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot line charts
df_plot = df.copy()

ncols = 2
nrows = int(round(df_plot.shape[1] / ncols, 0))

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, sharex=True, figsize=(14, 7))
for i, ax in enumerate(fig.axes):
        sns.lineplot(data = df_plot.iloc[:, i], ax=ax)
        ax.tick_params(axis=&quot;x&quot;, rotation=30, labelsize=10, length=0)
        ax.xaxis.set_major_locator(mdates.AutoDateLocator())
fig.tight_layout()
plt.show()</pre></div>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="5805" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/image-5-14/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2021/07/image-5.png" data-orig-size="999,496" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-5" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2021/07/image-5.png" src="https://www.relataly.com/wp-content/uploads/2021/07/image-5.png" alt="The Apple Stock's historical price data, including quotes, highs, lows, and volume" class="wp-image-5805" width="817" height="406" srcset="https://www.relataly.com/wp-content/uploads/2021/07/image-5.png 999w, https://www.relataly.com/wp-content/uploads/2021/07/image-5.png 300w, https://www.relataly.com/wp-content/uploads/2021/07/image-5.png 768w" sizes="(max-width: 817px) 100vw, 817px" /></figure>



<p>The line plots look as expected and reflect the Apple stock price history. Because we are fetching daily data from an API, please note that the lineplots will look different depending on when you run the code. </p>



<h3 class="wp-block-heading" id="h-step-3-preprocess-the-data">Step #3: Preprocess the Data</h3>



<p>Next, we prepare the data for the training process of our multi-output forecasting model. Preparing the data for multivariate forecasting involves several steps: </p>



<ul class="wp-block-list">
<li>Selecting features for model training</li>



<li>Scaling and splitting the data into separate sets for training and testing</li>



<li>Slicing the time series into several shifted training batches</li>
</ul>



<p>Remember that the steps are specific to our data and the use case. The steps required to prepare the data for a neural network with multiple outputs in time series forecasting will depend on the characteristics of your data and the requirements of your model. It is essential to consider these factors and tailor your data preparation accordingly and carefully.</p>



<h4 class="wp-block-heading">3.1 Basic Preparations</h4>



<p>We begin by creating a copy of the initial data and resetting the index.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Indexing Batches
df_train = df.sort_values(by=['Date']).copy()

# We safe a copy of the dates index, before we need to reset it to numbers
date_index = df_train.index

# We reset the index, so we can convert the date-index to a number-index
df_train = df_train.reset_index(drop=True).copy()
df_train.head(5)</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">			Open		High		Low			Close		Adj Close	Volume
Date						
2022-11-29	144.289993	144.809998	140.350006	141.169998	141.169998	83763800
2022-11-30	141.399994	148.720001	140.550003	148.029999	148.029999	111224400
2022-12-01	148.210007	149.130005	146.610001	148.309998	148.309998	71250400
2022-12-02	145.960007	148.000000	145.649994	147.809998	147.809998	65421400
2022-12-05	147.770004	150.919998	145.770004	146.630005	146.630005	68732400</pre></div>



<h4 class="wp-block-heading" id="h-3-2-feature-selection-and-scaling">3.2 Feature Selection and Scaling</h4>



<p>We proceed with feature selection. To keep things simple, we will use the features from the input data without any modifications. After selecting the features, we scale them to a range between 0 and 1. To ease unscaling the predictions after training, we create two different scalers: One for the training data, which takes five columns, and one for the output data that scales a single column (the Close Price). I have covered <a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/" target="_blank" rel="noreferrer noopener">feature engineering in a separate article</a> if you want to learn more about this topic.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def prepare_data(df):

    # List of considered Features
    FEATURES = ['Open', 'High', 'Low', 'Close', 'Volume']

    print('FEATURE LIST')
    print([f for f in FEATURES])

    # Create the dataset with features and filter the data to the list of FEATURES
    df_filter = df[FEATURES]
    
    # Convert the data to numpy values
    np_filter_unscaled = np.array(df_filter)
    #np_filter_unscaled = np.reshape(np_unscaled, (df_filter.shape[0], -1))
    print(np_filter_unscaled.shape)

    np_c_unscaled = np.array(df['Close']).reshape(-1, 1)
    
    return np_filter_unscaled, np_c_unscaled, df_filter
    
np_filter_unscaled, np_c_unscaled, df_filter = prepare_data(df_train)
                                          
# Creating a separate scaler that works on a single column for scaling predictions
# Scale each feature to a range between 0 and 1
scaler_train = MinMaxScaler()
np_scaled = scaler_train.fit_transform(np_filter_unscaled)
    
# Create a separate scaler for a single column
scaler_pred = MinMaxScaler()
np_scaled_c = scaler_pred.fit_transform(np_c_unscaled)   </pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">FEATURE LIST
['Open', 'High', 'Low', 'Close', 'Volume']
(3254, 5)</pre></div>



<p>The final step of the data preparation is to create the structure for the input data. This structure needs to match the input layer of the model architecture.</p>



<h4 class="wp-block-heading">3.3 Slicing the Data for a Model with Multiple In- and Outputs</h4>



<p>The code below starts a sliding window process that cuts the initial time series data into multiple slices, i.e., mini-batches. Each batch is a smaller fraction of the initial time series shifted by a single step. Because we will feed our model with multivariate input data, the time series consists of five input columns/features. Each batch comprises a period of 50 steps from the time series and an output sequence of ten consecutive values. To validate that the batches have the right shape, we visualize mini-batches in a line graph with their consecutive target values. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Set the input_sequence_length length - this is the timeframe used to make a single prediction
input_sequence_length = 50
# The output sequence length is the number of steps that the neural network predicts
output_sequence_length = 10 #

# Prediction Index
index_Close = df_train.columns.get_loc(&quot;Close&quot;)

# Split the training data into train and train data sets
# As a first step, we get the number of rows to train the model on 80% of the data 
train_data_length = math.ceil(np_scaled.shape[0] * 0.8)

# Create the training and test data
train_data = np_scaled[:train_data_length, :]
test_data = np_scaled[train_data_length - input_sequence_length:, :]

# The RNN needs data with the format of [samples, time steps, features]
# Here, we create N samples, input_sequence_length time steps per sample, and f features
def partition_dataset(input_sequence_length, output_sequence_length, data):
    x, y = [], []
    data_len = data.shape[0]
    for i in range(input_sequence_length, data_len - output_sequence_length):
        x.append(data[i-input_sequence_length:i,:]) #contains input_sequence_length values 0-input_sequence_length * columns
        y.append(data[i:i + output_sequence_length, index_Close]) #contains the prediction values for validation (3rd column = Close),  for single-step prediction
    
    # Convert the x and y to numpy arrays
    x = np.array(x)
    y = np.array(y)
    return x, y

# Generate training data and test data
x_train, y_train = partition_dataset(input_sequence_length, output_sequence_length, train_data)
x_test, y_test = partition_dataset(input_sequence_length, output_sequence_length, test_data)

# Print the shapes: the result is: (rows, training_sequence, features) (prediction value, )
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

# Validate that the prediction value and the input match up
# The last close price of the second input sample should equal the first prediction value
nrows = 3 # number of shifted plots
fig, ax = plt.subplots(nrows=nrows, ncols=1, figsize=(16, 8))
for i, ax in enumerate(fig.axes):
    xtrain = pd.DataFrame(x_train[i][:,index_Close], columns={f'x_train_{i}'})
    ytrain = pd.DataFrame(y_train[i][:output_sequence_length-1], columns={f'y_train_{i}'})
    ytrain.index = np.arange(input_sequence_length, input_sequence_length + output_sequence_length-1)
    xtrain_ = pd.concat([xtrain, ytrain[:1].rename(columns={ytrain.columns[0]:xtrain.columns[0]})])
    df_merge = pd.concat([xtrain_, ytrain])
    sns.lineplot(data = df_merge, ax=ax)
plt.show</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">(2544, 50, 5) (2544, 10)
(640, 50, 5) (640, 10)
&lt;function matplotlib.pyplot.show(close=None, block=None)&gt;</pre></div>



<figure class="wp-block-image size-full is-resized"><img decoding="async" data-attachment-id="8670" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png" data-orig-size="939,465" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png" src="https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png" alt="batch test visualizations, deep neural networks for multi-output stock market forecasting" class="wp-image-8670" width="891" height="441" srcset="https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png 939w, https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/batch-test-visualizations-deep-neural-networks-for-multi-output-stock-market-forecasting.png 768w" sizes="(max-width: 891px) 100vw, 891px" /></figure>



<h3 class="wp-block-heading" id="h-step-4-prepare-the-neural-network-architecture-and-train-the-multi-output-regression-model">Step #4: Prepare the Neural Network Architecture and Train the Multi-Output Regression Model</h3>



<p>Now that we have the training data prepared and ready, the next step is to configure the architecture of the multi-out neural network. Because we will be using multiple input series, our model is, in fact, a multivariate architecture so that it corresponds to the input training batches. </p>



<h4 class="wp-block-heading" id="h-4-1-configuring-and-training-the-model">4.1 Configuring and Training the Model</h4>



<p>We choose a comparably simple architecture with only two LSTM layers and two additional dense layers. The first dense layer has 20 neurons, and the second layer is the output layer, which has ten output neurons. If you wonder how I got to the number of neurons in the third layer, I conducted several experiments and found that this number leads to solid results. </p>



<p>To ensure that the architecture matches our input data&#8217;s structure, we reuse the variables for the previous code section (n_input_neurons, n_output_neurons. The input sequence length is 50, and the output sequence (the steps for the period we want to predict) is ten.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Configure the neural network model
model = Sequential()
n_output_neurons = output_sequence_length

# Model with n_neurons = inputshape Timestamps, each with x_train.shape[2] variables
n_input_neurons = x_train.shape[1] * x_train.shape[2]
print(n_input_neurons, x_train.shape[1], x_train.shape[2])
model.add(LSTM(n_input_neurons, return_sequences=True, input_shape=(x_train.shape[1], x_train.shape[2]))) 
model.add(LSTM(n_input_neurons, return_sequences=False))
model.add(Dense(20))
model.add(Dense(n_output_neurons))

# Compile the model
model.compile(optimizer='adam', loss='mse')</pre></div>



<p>After configuring the model architecture, we can initiate the training process and illustrate how the loss develops over the training epochs. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Training the model
epochs = 10
batch_size = 16
early_stop = EarlyStopping(monitor='loss', patience=5, verbose=1)
history = model.fit(x_train, y_train, 
                    batch_size=batch_size, 
                    epochs=epochs,
                    validation_data=(x_test, y_test)
                   )
                    
                    #callbacks=[early_stop])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Epoch 1/5
159/159 [==============================] - 7s 14ms/step - loss: 0.0047 - val_loss: 0.0262
Epoch 2/5
159/159 [==============================] - 2s 11ms/step - loss: 3.6759e-04 - val_loss: 0.0097
Epoch 3/5
159/159 [==============================] - 2s 11ms/step - loss: 1.5222e-04 - val_loss: 0.0056
Epoch 4/5
159/159 [==============================] - 2s 11ms/step - loss: 1.0327e-04 - val_loss: 0.0031
Epoch 5/5
159/159 [==============================] - 2s 11ms/step - loss: 1.1690e-04 - val_loss: 0.0026</pre></div>



<h4 class="wp-block-heading" id="h-4-2-loss-curve">4.2 Loss Curve</h4>



<p>Next, we plot the loss curve, which represents the amount of error between the model&#8217;s predicted values and the actual values in the training data. A lower loss value indicates that the model makes more accurate predictions on the training data. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot training &amp; validation loss values
fig, ax = plt.subplots(figsize=(10, 5), sharex=True)
plt.plot(history.history[&quot;loss&quot;])
plt.title(&quot;Model loss&quot;)
plt.ylabel(&quot;Loss&quot;)
plt.xlabel(&quot;Epoch&quot;)
ax.xaxis.set_major_locator(plt.MaxNLocator(epochs))
plt.legend([&quot;Train&quot;, &quot;Test&quot;], loc=&quot;upper left&quot;)
plt.grid()
plt.show()</pre></div>



<figure class="wp-block-image size-full is-resized is-style-default"><img decoding="async" data-attachment-id="5856" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/image-4-16/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2021/08/image-4.png" data-orig-size="628,333" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-4" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2021/08/image-4.png" src="https://www.relataly.com/wp-content/uploads/2021/08/image-4.png" alt="loss curve after training the multi-output neural network, keras, multi-step time series regression" class="wp-image-5856" width="720" height="381" srcset="https://www.relataly.com/wp-content/uploads/2021/08/image-4.png 628w, https://www.relataly.com/wp-content/uploads/2021/08/image-4.png 300w" sizes="(max-width: 720px) 100vw, 720px" /></figure>



<p>As we can see, the loss curve drops quickly during training, which typically means that the model is quickly learning to make accurate predictions.</p>



<h3 class="wp-block-heading" id="h-step-5-evaluate-model-performance">Step #5 Evaluate Model Performance</h3>



<p>Now that we have trained the model, we can make forecasts on the test data and use traditional regression metrics such as the MAE, MAPE, or MDAPE to measure the performance of our model. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the predicted values
y_pred_scaled = model.predict(x_test)

# Unscale the predicted values
y_pred = scaler_pred.inverse_transform(y_pred_scaled)
y_test_unscaled = scaler_pred.inverse_transform(y_test).reshape(-1, output_sequence_length)

# Mean Absolute Error (MAE)
MAE = mean_absolute_error(y_test_unscaled, y_pred)
print(f'Median Absolute Error (MAE): {np.round(MAE, 2)}')

# Mean Absolute Percentage Error (MAPE)
MAPE = np.mean((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled))) * 100
print(f'Mean Absolute Percentage Error (MAPE): {np.round(MAPE, 2)} %')

# Median Absolute Percentage Error (MDAPE)
MDAPE = np.median((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled)) ) * 100
print(f'Median Absolute Percentage Error (MDAPE): {np.round(MDAPE, 2)} %')


def prepare_df(i, x, y, y_pred_unscaled):
    # Undo the scaling on x, reshape the testset into a one-dimensional array, so that it fits to the pred scaler
    x_test_unscaled_df = pd.DataFrame(scaler_pred.inverse_transform((x[i]))[:,index_Close]).rename(columns={0:'x_test'})
    
    y_test_unscaled_df = []
    # Undo the scaling on y
    if type(y) == np.ndarray:
        y_test_unscaled_df = pd.DataFrame(scaler_pred.inverse_transform(y)[i]).rename(columns={0:'y_test'})

    # Create a dataframe for the y_pred at position i, y_pred is already unscaled
    y_pred_df = pd.DataFrame(y_pred_unscaled[i]).rename(columns={0:'y_pred'})
    return x_test_unscaled_df, y_pred_df, y_test_unscaled_df


def plot_multi_test_forecast(x_test_unscaled_df, y_test_unscaled_df, y_pred_df, title): 
    # Package y_pred_unscaled and y_test_unscaled into a dataframe with columns pred and true   
    if type(y_test_unscaled_df) == pd.core.frame.DataFrame:
        df_merge = y_pred_df.join(y_test_unscaled_df, how='left')
    else:
        df_merge = y_pred_df.copy()
    
    # Merge the dataframes 
    df_merge_ = pd.concat([x_test_unscaled_df, df_merge]).reset_index(drop=True)
    
    # Plot the linecharts
    fig, ax = plt.subplots(figsize=(20, 8))
    plt.title(title, fontsize=12)
    ax.set(ylabel = stockname + &quot;_stock_price_quotes&quot;)
    sns.lineplot(data = df_merge_, linewidth=2.0, ax=ax)

# Creates a linechart for a specific test batch_number and corresponding test predictions
batch_number = 50
x_test_unscaled_df, y_pred_df, y_test_unscaled_df = prepare_df(i, x_test, y_test, y_pred)
title = f&quot;Predictions vs y_test - test batch number {batch_number}&quot;
plot_multi_test_forecast(x_test_unscaled_df, y_test_unscaled_df, y_pred_df, title) </pre></div>



<figure class="wp-block-image size-large is-style-default"><img decoding="async" width="1024" height="419" data-attachment-id="8667" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/multioutput-regression-deep-neural-networks-test-predictions/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png" data-orig-size="1170,479" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="multioutput-regression-deep-neural-networks-test-predictions" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png" src="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions-1024x419.png" alt="A line chart showing predictions and test data, multioutput regression deep neural networks test predictions" class="wp-image-8667" srcset="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png 1024w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png 768w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-test-predictions.png 1170w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The quality of the predictions is acceptable, considering that this tutorial aimed not to achieve excellent predictions but to demonstrate the process and architecture of training a multi-output regression. So, there is certainly room for improvement. Feel free to experiment with different features or try other hyperparameters and neural network layers.</p>



<h3 class="wp-block-heading" id="h-step-6-create-a-new-forecast">Step #6 Create a New Forecast</h3>



<p>Finally, let&#8217;s create a forecast on a new dataset. We take the scaled dataset from section 2 (np_scaled) and extract a series with the latest 50 values. The data is reshaped into a 3D array with shape (1, 50, 5) to match the expected input shape of the model. We use these values to generate a new prediction for the next ten days using the predict method. We store the result in the y_pred_scaled variable. In addition, we need to transform the predictions back to the original scale. We do this by using the inverse_transform method of the scaler_pred object, which was fit on the training data. Finally, we visualize the multi-step forecast in another line chart. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the latest input batch from the test dataset, which is contains the price values for the last ten trading days
x_test_latest_batch = np_scaled[-50:,:].reshape(1,50,5)

# Predict on the batch
y_pred_scaled = model.predict(x_test_latest_batch)
y_pred_unscaled = scaler_pred.inverse_transform(y_pred_scaled)

# Prepare the data and plot the input data and the predictions
x_test_unscaled_df, y_test_unscaled_df, _ = prepare_df(0, x_test_latest_batch, '', y_pred_unscaled)
plot_multi_test_forecast(x_test_unscaled_df, '', y_test_unscaled_df, &quot;x_new Vs. y_new_pred&quot;)</pre></div>



<figure class="wp-block-image size-large is-style-default"><img decoding="async" width="1024" height="420" data-attachment-id="8666" data-permalink="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/multioutput-regression-deep-neural-networks/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png" data-orig-size="1167,479" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="multioutput-regression-deep-neural-networks" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png" src="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks-1024x420.png" alt="multioutput regression deep neural networks - x_text and y_pred" class="wp-image-8666" srcset="https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png 1024w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png 768w, https://www.relataly.com/wp-content/uploads/2022/05/multioutput-regression-deep-neural-networks.png 1167w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p></p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>In this tutorial, we demonstrated how to use multiple output neural networks to make predictions at different time steps. We first discussed the architecture of a recurrent neural network and how it can be used to process sequential data. We then showed how to properly preprocess the data and split it into training and test sets for training a multi-output regression model.</p>



<p>Next, we trained a model to predict the stock price of Apple ten steps into the future using historical data. We also discussed how to use the trained model to make multi-step predictions on new data and how to visualize the results.</p>



<p>To further improve the performance of the model, you can experiment with different hyperparameters and adjust the model architecture. For example, adding more neurons to the output layers will increase the prediction horizon, but remember that prediction error will also increase as the horizon lengthens. You can also try using different activation functions or adding more layers to the model to see how it affects the performance.</p>



<p>I hope this article was helpful in understanding multi-output neural networks better. If you have any questions or comments, please let me know.</p>



<h2 class="wp-block-heading" id="h-sources-and-further-reading">Sources and Further Reading</h2>



<ol class="wp-block-list">
<li><a href="https://amzn.to/3MyU6Tj" target="_blank" rel="noreferrer noopener">Charu C. Aggarwal (2018) Neural Networks and Deep Learning</a></li>



<li><a href="https://amzn.to/3yIQdWi" target="_blank" rel="noreferrer noopener">Jansen (2020) Machine Learning for Algorithmic Trading: Predictive models to extract signals from market and alternative data for systematic trading strategies with Python</a></li>



<li><a href="https://amzn.to/3S9Nfkl" target="_blank" rel="noreferrer noopener">Aurélien Géron (2019) Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems </a></li>



<li><a href="https://amzn.to/3EKidwE" target="_blank" rel="noreferrer noopener">David Forsyth (2019) Applied Machine Learning Springer</a></li>



<li><a href="https://amzn.to/3MAy8j5" target="_blank" rel="noreferrer noopener">Andriy Burkov (2020) Machine Learning Engineering</a></li>
</ol>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>



<p>If you want to learn about an alternative approach to univariate stock market forecasting, consider taking a look <a href="https://www.relataly.com/time-series-forecasting-using-facebook-prophet-in-python/10351/" target="_blank" rel="noreferrer noopener">at Facebook Prophet</a> or <a href="https://www.relataly.com/forecasting-beer-sales-with-arima-in-python/2884/" target="_blank" rel="noreferrer noopener">ARIMA models</a></p>
<p>The post <a href="https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/">Stock Market Forecasting Neural Networks for Multi-Output Regression in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/stock-price-prediction-multi-output-regression-using-neural-networks-in-python/5800/feed/</wfw:commentRss>
			<slash:comments>31</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5800</post-id>	</item>
		<item>
		<title>Image Classification with Convolutional Neural Networks &#8211; Classifying Cats and Dogs in Python</title>
		<link>https://www.relataly.com/image-classification-with-deep-learning/2485/</link>
					<comments>https://www.relataly.com/image-classification-with-deep-learning/2485/#respond</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Sun, 13 Dec 2020 14:09:31 +0000</pubDate>
				<category><![CDATA[Classification (two-class)]]></category>
		<category><![CDATA[Convolutional Neural Network (CNN)]]></category>
		<category><![CDATA[Data Sources]]></category>
		<category><![CDATA[Image Recognition]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Use Cases]]></category>
		<category><![CDATA[Beginner Tutorials]]></category>
		<category><![CDATA[Computer Vision]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Image Dataset]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<category><![CDATA[Two-Label Classification]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=2485</guid>

					<description><![CDATA[<p>This tutorial shows how to use Convolutional Neural Networks (CNNs) with Python for image classification. CNNs belong to the field of deep learning, a subarea of machine learning, and have become a cornerstone to many exciting innovations. There are endless applications, from self-driving cars over biometric security to automated tagging in social media. And the ... <a title="Image Classification with Convolutional Neural Networks &#8211; Classifying Cats and Dogs in Python" class="read-more" href="https://www.relataly.com/image-classification-with-deep-learning/2485/" aria-label="Read more about Image Classification with Convolutional Neural Networks &#8211; Classifying Cats and Dogs in Python">Read more</a></p>
<p>The post <a href="https://www.relataly.com/image-classification-with-deep-learning/2485/">Image Classification with Convolutional Neural Networks &#8211; Classifying Cats and Dogs in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>This tutorial shows how to use Convolutional Neural Networks (CNNs) with Python for image classification. CNNs belong to the field of deep learning, a subarea of machine learning, and have become a cornerstone to many exciting innovations. There are endless applications, from self-driving cars over biometric security to automated tagging in social media. And the importance of CNNs grows steadily! So there are plenty of reasons to understand how this technology works and how we can implement it. </p>



<p>This article proceeds as follows: The first part introduces the core concepts behind CNNs and explains their use in image classification. The second part is a hands-on tutorial in which you will build your own CNN to distinguish images of cats and dogs. This tutorial develops a model that achieves around 82% validation accuracy. We will work with TensorFlow and Python to integrate different layers, such as Convolution Layers, Dense layers, and MaxPooling. Furthermore, we will prevent the network from overfitting the training data by using Dropout between the layers. We will also load the model and make predictions on a fresh set of images. Finally, we analyze and illustrate the performance of our image classifier. </p>



<p>Also: <a href="https://www.relataly.com/automated-prompt-generation-for-dall-e-using-chatgpt-in-python-a-step-by-step-api-tutorial/12143/" target="_blank" rel="noreferrer noopener">Generating Detailed Images with OpenAI DALL-E and ChatGPT in Python: A Step-By-Step API Tutorial</a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h2 class="wp-block-heading" id="h-image-classification-with-convolutional-neural-networks">Image Classification with Convolutional Neural Networks</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The history of image recognition dates back to the mid-1960s when the first attempts were made to identify objects by coding their characteristic shapes and lines. However, this task turned out to be incredibly complex. Our human brain is trained so well to recognize things that one can easily forget how diverse the observation conditions can be. Here are some examples:</p>



<ul class="wp-block-list">
<li>Fotos can be taken from various viewpoints</li>



<li>Living things can have multiple forms and poses</li>



<li>Objects come in different forms, colors, and sizes</li>



<li>The picture may hide parts of the things in the picture</li>



<li>The light conditions vary from image  to image</li>



<li>There may be one or multiple objects in the same image</li>
</ul>



<p>At the beginning of the 1990s, the focus of research shifted to statistical approaches and learning algorithms.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large"><img decoding="async" width="512" height="512" data-attachment-id="13345" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/machine_learning_computer_vision_dazzling_magic_neural_network-min/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png" data-orig-size="1024,1024" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="machine_learning_computer_vision_dazzling_magic_neural_network-min" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png" src="https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min-512x512.png" alt="The idea of computer vision is inspired by the fact that the visual cortex has cells activated by specific shapes and their orientation in the visual field. " class="wp-image-13345" srcset="https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png 512w, https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png 300w, https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png 140w, https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png 768w, https://www.relataly.com/wp-content/uploads/2023/03/machine_learning_computer_vision_dazzling_magic_neural_network-min.png 1024w" sizes="(max-width: 512px) 100vw, 512px" /><figcaption class="wp-element-caption">The idea of computer vision is inspired by the fact that the visual cortex has cells activated by specific shapes and their orientation in the visual field. </figcaption></figure>
</div>
</div>



<p></p>



<h3 class="wp-block-heading" id="h-the-emergence-of-cnns">The Emergence of CNNs</h3>



<p>The basic concept of a neural network in computer vision has existed since the 1980s. It goes back to research from Hubel and Wiesel on the emergence of a cat&#8217;s visual system. They found that the visual cortex has cells activated by specific shapes and their orientation in the visual field. Some of their findings inspired the development of crucial computer vision technologies, such as, for example, hierarchical features with different levels of abstraction [1, 2]. However, it took another three decades of research and the availability of faster computers before the emergence of modern CNNs.</p>



<p>The year 2012  was a defining moment for the use of CNNs in image recognition. This year, for the first time, CNN won the <a href="http://www.image-net.org/challenges/LSVRC/" target="_blank" rel="noreferrer noopener">ILSVRC </a>competition for computer vision. The challenge was classifying more than a hundred thousand images into 1000 object categories. With an error rate of only 15,3%, the succeeding model was a CNN called &#8220;AlexNet.&#8221;.</p>



<p>AlexNet was the first model to achieve more than 75% accuracy. In the same year, CNNs succeeded in several other competitions. For example, in 2015, the CNN ResNet exceeded human performance in the ILSVRC competition. Only a decade ago, this achievement was considered almost impossible. So how was this performance increase possible? To understand this surge in performance, let us first look at what a picture is.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2653" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-15-5/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-15.png" data-orig-size="1081,506" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-15" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-15.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-15-1024x479.png" alt="" class="wp-image-2653" width="848" height="395" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-15.png 300w, https://www.relataly.com/wp-content/uploads/2020/12/image-15.png 768w" sizes="(max-width: 848px) 100vw, 848px" /><figcaption class="wp-element-caption">Top-performing models in the ImageNet image classification challenge (Alyafeai &amp; Ghouti, 2019)</figcaption></figure>



<h3 class="wp-block-heading" id="h-what-is-an-image">What is an Image?</h3>



<p>A digital image is a three-dimensional array of integer values. One dimension of this array represents the pixel width, and one dimension represents the height of the picture. The third dimension contains the color depth, defined by the image format. As shown below, we can thus represent the format of a digital image as &#8220;width x height x depth.&#8221; Next, let&#8217;s have a quick look at different image formats.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2649" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-11-6/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-11.png" data-orig-size="1152,437" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-11" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-11.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-11-1024x388.png" alt="an image is a multidimensional integer array" class="wp-image-2649" width="861" height="326" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-11.png 1024w, https://www.relataly.com/wp-content/uploads/2020/12/image-11.png 300w, https://www.relataly.com/wp-content/uploads/2020/12/image-11.png 768w, https://www.relataly.com/wp-content/uploads/2020/12/image-11.png 1152w" sizes="(max-width: 861px) 100vw, 861px" /><figcaption class="wp-element-caption">A digital image is a multidimensional integer array.</figcaption></figure>



<h3 class="wp-block-heading" id="h-overview-of-different-image-formats">Overview of Different Image Formats</h3>



<p>We can train CNNs with different image formats, but the input data are always multidimensional arrays of integer values. One of the most commonly used color formats in deep learning is &#8220;RGB.&#8221; RGB stands for the three color channels: &#8220;Red,&#8221; &#8220;Green,&#8221; and &#8220;Blue.&#8221; RGB images are divided into three layers of integer values, one layer for each color channel—the integer values of a 16-bit RGB image in each layer range from 1 to 255. Together, the three layers can reproduce 65,536 different colors. </p>



<p>In contrast to RGB images, grey-scale images only have a single color layer. This layer resembles the brightness of each pixel in the image. Consequently, the format of a grey-scale image is width x height x 1. Using grey-scale images or images with black and white shades instead of RGB images can speed up the training process because less data needs to be processed. However, image data with multiple color channels provide the model with more information, leading to better predictions. The RGB format is often a good choice between prediction quality and performance. Next, let&#8217;s look at how CNNs handle digital images in the learning process.</p>



<h3 class="wp-block-heading" id="h-convolutional-neural-networks">Convolutional Neural Networks</h3>



<p>As mentioned before, a CNN is a specific form of an artificial neural network. The main difference between the CNN and the standard multi-layer perceptron is their convolutional layers. CNNs can have other layers, but the convolutions make a CNN so good at detecting objects. They allow the network to identify patterns based on features that work regardless of where in the image they occur. Let&#8217;s see how this works in more detail.</p>



<h4 class="wp-block-heading" id="h-convolutional-layers">Convolutional Layers</h4>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p>Convolutional layers use a rasterizing technique that breaks down an image into smaller groups of pixels called filters. Filters act as feature detectors from the original image. The primary purpose is to extract meaningful features from the input images.</p>



<p>During the training, the CNN slides the filter over image locations and calculates the dot product for each feature at a time. The results of these calculations are stored in a so-called feature map (sometimes called an activation map). A feature map represents where in the image a particular feature was identified. Subsequently, the values from the feature map are transformed with an activation function (usually ReLu), and the algorithm uses them as input to the next layer.</p>


<div class="wp-block-image">
<figure class="alignleft size-large is-resized"><img decoding="async" data-attachment-id="6596" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-1/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/image-1.png" data-orig-size="1237,502" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-1" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/image-1.png" src="https://www.relataly.com/wp-content/uploads/2022/04/image-1-1024x416.png" alt="Illustration of operations in the convolutional layers" class="wp-image-6596" width="811" height="332"/><figcaption class="wp-element-caption">Illustration of operations in the convolutional layers</figcaption></figure>
</div></div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p>Features become more complex with the increasing depth of the network. In the first layer of the network, convolutions will detect generic geometric forms and low-level features based on edges, corners, squares, or circles. The subsequent layers of the network will look at more sophisticated shapes and may, for example, include features that resemble the form of an eye of a cat or the nose of a dog. In this way, convolutions provide the network with features at different levels of detail that enable powerful detection patterns.</p>


<div class="wp-block-image">
<figure class="alignleft size-large is-resized"><img decoding="async" data-attachment-id="2661" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-16-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-16.png" data-orig-size="1908,819" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-16" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-16.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-16-1024x440.png" alt="Convolutions at the example of an image that contains the number &quot;3&quot;" class="wp-image-2661" width="874" height="374" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-16.png 300w, https://www.relataly.com/wp-content/uploads/2020/12/image-16.png 768w, https://www.relataly.com/wp-content/uploads/2020/12/image-16.png 1536w, https://www.relataly.com/wp-content/uploads/2020/12/image-16.png 1908w" sizes="(max-width: 874px) 100vw, 874px" /><figcaption class="wp-element-caption">Exemplary convolutions of an image that contains the number &#8220;3.&#8221;</figcaption></figure>
</div></div>
</div>



<h4 class="wp-block-heading" id="h-pooling-downsampling">Pooling / Downsampling</h4>



<p>A convolutional layer is usually followed by a pooling operation, which reduces the amount of data by filtering unnecessary information. This process is also called downsampling or subsampling. There are various forms of pooling. In the most common variant &#8211; max-pooling &#8211; only the highest value in a predefined grid (e.g., 2&#215;2) is processed, and the remaining values are discarded. For example, imagine a 2&#215;2 grid with values 0.1, 0.5, 0.4, and 0.8. The algorithm would only process the 0,8 further for this grid and use it as part of the input to the next layer. The advantages of pooling are reduced data and faster training times. Because pooling minimizes the complexity of the network, it allows for the construction of deeper architectures with more layers. In addition, pooling offers a certain protection against overfitting during training.</p>



<h4 class="wp-block-heading" id="h-dropout">Dropout</h4>



<p>Dropout is another technique that helps prevent the network from overfitting the training data. When we activate Dropout for a layer, the algorithm will remove a random number of neurons from the layer per training step. As a result, the network needs to learn patterns that give less weight to individual layers and thus generalize better. The dropout rate controls the percentage of switched-off neurons in each training iteration. We can configure Dropout for each layer separately. </p>



<p>CNNs with many layers and training epochs tend to overfit the training data. Especially here, Dropout is crucial to avoid overfitting and to achieve good prediction results with data that the network does not know yet. A typical value for the rate lies between 10% to 30%.</p>



<h4 class="wp-block-heading" id="h-multi-layer-perceptron-mlp">Multi-Layer Perceptron (MLP)</h4>



<p>The CNN architecture ends with multiple dense layers that are fully connected. The layers are part of a Multilayer Perception (MLP), which has the task of dense down the results from the previous convolutions and outputting one of the multiple classes. Consequently, the number of neurons in the final dense layer usually corresponds to the number of different classes to be predicted. It is also possible to use a single neuron in the final layer for two-class prediction problems. In this case, the last neuron outputs a binary label of 0 or 1.</p>



<h2 class="wp-block-heading" id="h-building-a-cnn-with-tensorflow-that-classifies-cats-and-dogs">Building a CNN with Tensorflow that Classifies Cats and Dogs</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Now that you are familiar with the basic concepts behind convolutional neural networks, we can commence with the practical part and build an image classifier. In the following, we will train a CNN to distinguish images of cats and dogs. We first define a CNN model and then feed it a few thousand photos from a public dataset with labeled images of cats and dogs.</p>



<p>Distinguishing cats and dogs may not sound difficult, but many challenges exist. Imagine the almost infinite circumstances in which animals can be photographed, not to mention the many forms a cat can take. These variations lead to the fact that even humans sometimes confuse a cat with a dog or vice versa. So don&#8217;t expect our model to be perfect right from the start. Our model will score around 82% accuracy on the validation dataset.</p>



<p>The code is available on the GitHub repository.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_d70aa4-6e"><a class="kb-button kt-button button kb-btn_b926ba-d4 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/06%20Computer%20Vision/200%20Classifying%20Cats%20%26%20Dogs%20Binary.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_80b142-4f kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly GitHub Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image is-resized"><img decoding="async" src="https://www.relataly.com/wp-content/uploads/2022/04/Image-Recognition-Convolutional-Neural-Networks.png" alt="Image Recognition Convolutional Neural Networks - classifying cats and dogs python " width="382" height="125"/><figcaption class="wp-element-caption">Cat or Dog? That&#8217;s what our CNN will predict.</figcaption></figure>
</div>
</div>



<div style="height:29px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p>Before starting the coding part, make sure that you have set up your <a href="https://www.python.org/downloads/" target="_blank" rel="noreferrer noopener">Python 3</a> environment and required packages. If you don&#8217;t have an environment, you can follow&nbsp;<a href="https://www.relataly.com/anaconda-python-environment-machine-learning/1663/" target="_blank" rel="noreferrer noopener">this tutorial</a>&nbsp;to set up the&nbsp;<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda environment</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><a href="https://docs.python.org/3/library/math.html" target="_blank" rel="noreferrer noopener">math</a></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>
</ul>



<p>In addition, we will be using <em><a href="https://keras.io/" target="_blank" rel="noreferrer noopener">Keras&nbsp;</a></em>(2.0 or higher) with <a href="https://www.tensorflow.org/" target="_blank" rel="noreferrer noopener"><em>Tensorflow</em> </a>backend and the machine learning library <a href="https://scikit-learn.org/stable/" target="_blank" rel="noreferrer noopener">Scikit-learn</a>.</p>



<p>You can install packages using console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>



<h4 class="wp-block-heading" id="h-download-the-dataset">Download the Dataset</h4>



<p>We will train our image classification model with a public dataset from <a href="http://www.kaggle.com" target="_blank" rel="noreferrer noopener">Kaggle.com</a>. The dataset contains more than 25.000 JPG pictures of cats and dogs. The images are uniformly named and numbered, for example, dog.1.jpg, dog.2.jpg, dog.3.jpg, cat.1.jpg, cat.2.jpg, and so on. You can download the picture set directly from Kaggle: <a href="https://www.kaggle.com/c/dogs-vs-cats/overview" target="_blank" rel="noreferrer noopener">cats-vs-dogs</a>. </p>



<h4 class="wp-block-heading" id="h-setup-the-folder-structure">Setup the Folder Structure</h4>



<p>There are different ways data can be structured and loaded during model training. One approach (1) is to split the images into classes and create a separate folder for each class, class_a, class_b, etc. Another method (2) is to put all images into a single folder and define a DataFrame that splits the data into test and train. Because the cats and dogs dataset files already contain the classes in their name, I decided to go for the second approach. </p>



<p>Before we begin with the coding part, we create a folder structure that looks as follows:</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2676" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-17-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-17.png" data-orig-size="532,286" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-17" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-17.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-17.png" alt="structure of the data that we will use to train the convolutional neural network" class="wp-image-2676" width="409" height="220" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-17.png 532w, https://www.relataly.com/wp-content/uploads/2020/12/image-17.png 300w" sizes="(max-width: 409px) 100vw, 409px" /><figcaption class="wp-element-caption">The folder structure of our cats and dogs prediction project</figcaption></figure>



<p>If you want to use the standard pathways given in the python tutorial, make sure that your notebook resides in the parent folder of the &#8220;data&#8221; folder.</p>



<p>After you have created the folder structure, open the cats-vs-dogs zip file. The ZIP file contains the folders &#8220;train,&#8221; &#8220;test,&#8221; and &#8220;sample.&#8221; Unzip the JPG files from the &#8220;train&#8221; (20.000 images) and the &#8220;test&#8221; folder (5.000 pictures) to the &#8220;train&#8221; folder of your project. Afterward, the train folder should contain 25.000 images. The sample folder is intended to include your sample images, for example, of your pet. We will later use the images from the sample folder to test the model on new real-world data. </p>



<p>We have fulfilled all requirements and can start with the coding part.</p>



<h3 class="wp-block-heading" id="h-step-1-make-imports-and-check-training-device">Step #1 Make Imports and Check Training Device</h3>



<p>We begin by setting up the imports for this project. I have put the package imports at the beginning to give you a  quick overview of the packages you need to install.</p>



<p>Using the GPU instead of the CPU allows for faster training times. However, setting up Tensorflow to work with the GPUs can cause problems. Not everyone has a GPU; in this case, TensorFlow should usually automatically run all code on the CPU. However, should you for any reason prefer to manually switch to CPU training, change [&#8220;CUDA_VISIBLE_DEVICES&#8221;]= &#8220;1&#8221; to &#8220;-1&#8221;. As a result, Tensorflow will run all code on the CPU and ignore all available GPUs. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">import os
#os.environ[&quot;CUDA_VISIBLE_DEVICES&quot;]=&quot;-1&quot; 

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Convolution2D, MaxPooling2D, ZeroPadding2D
from tensorflow.keras.layers import Conv2D, Activation, Dropout, Flatten, Dense, BatchNormalization
from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau
from tensorflow.keras.metrics import Accuracy
from tensorflow.keras import regularizers
from tensorflow.keras.optimizers import SGD, Adam
from tensorflow.python.client import device_lib
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score

tf.config.allow_growth = True
tf.config.per_process_gpu_memory_fraction = 0.9

from random import randint
import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import seaborn as sns
from PIL import Image
import random as rdn</pre></div>



<p>Running the command below checks the TensorFlow version and the number of available GPUs in our system. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># check the tensorflow version
print('Tensorflow Version: ' + tf.__version__)

# check the number of available GPUs
physical_devices = tf.config.list_physical_devices('GPU')
print(&quot;Num GPUs:&quot;, len(physical_devices))</pre></div>



<pre class="wp-block-preformatted">Tensorflow Version: 2.4.0-rc3
Num GPUs: 1</pre>



<p>My GPU is an RTX 3080. When I wrote this article, the GPU was not yet supported by the standard TensorFlow release. I have therefore used the pre-release version of TensorFlow (2.4.0-rc3). I expect the following standard release (2.3) to work fine. </p>



<p>In my case, the GPU check returns one because I have a single GPU on my computer. If TensorFlow doesn&#8217;t recognize any GPU, this command will return 0. Tensorflow will then run on the CPU.</p>



<h3 class="wp-block-heading" id="h-step-2-define-the-prediction-classes">Step #2 Define the Prediction Classes</h3>



<p>Next, we will define the path to the folders that contain our train and validation images. In addition, we will define a Dataframe &#8220;image_df,&#8221; which has all the pictures from the &#8220;train&#8221; folder. With the help of this Dataframe, we can later split the data simply by defining which images from the train folder contain the training dataset and which belong to the test dataset. Important note: the dataframe &#8220;image_df&#8221; only includes the names of the images and the classes, but not the photos themselves.</p>



<p>It&#8217;s good to check the distribution of classes in the training data set. For this purpose, we create a bar plot, which illustrates the number of both classes in the image data. And yes, I admit, I choose some custom colors to make it look fancy.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># set the directory for train and validation images
train_path = 'data/images/cats-and-dogs/train/'
#test_path = 'data/cats-and-dogs/test/'

# function to create a list of image labels 
def createImageDf(path):
    filenames = os.listdir(path)
    categories = []

    for fname in filenames:
        category = fname.split('.')[0]
        if category == 'dog':
            categories.append(1)
        else:
            categories.append(0)
    df = pd.DataFrame({
        'filename':filenames,
        'category':categories
    })
    return df

# display the header of the train_df dataset
image_df = createImageDf(train_path)
image_df.head(5)

sns.countplot(y='category', data=image_df, palette=['#2FE5C7',&quot;#2F8AE5&quot;], orient=&quot;h&quot;)</pre></div>



<p></p>



<figure class="wp-block-image size-full"><img decoding="async" width="376" height="262" data-attachment-id="11572" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-9-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/12/image-9.png" data-orig-size="376,262" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-9" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/12/image-9.png" src="https://www.relataly.com/wp-content/uploads/2022/12/image-9.png" alt="" class="wp-image-11572" srcset="https://www.relataly.com/wp-content/uploads/2022/12/image-9.png 376w, https://www.relataly.com/wp-content/uploads/2022/12/image-9.png 300w" sizes="(max-width: 376px) 100vw, 376px" /></figure>



<p>The number of images in the two classes is balanced, so we don&#8217;t need to rebalance the data. That&#8217;s nice!</p>



<h3 class="wp-block-heading" id="h-step-3-plot-sample-images">Step #3 Plot Sample Images</h3>



<p>I prefer not to jump directly into preprocessing and check that the data has been correctly loaded. We will do this by plotting some random images from the train folder. This step is not necessary, but it&#8217;s a best practice.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">n_pictures = 16 # number of pictures to be shown
columns = int(n_pictures / 2)
rows = 2
plt.figure(figsize=(40, 12))
for i in range(n_pictures):
    num = i + 1
    ax = plt.subplot(rows, columns, i + 1)
    if i &lt; columns:
        image_name = 'cat.' + str(rdn.randint(1, 1000)) + '.jpg'
    else: 
        image_name = 'dog.' + str(rdn.randint(1, 1000)) + '.jpg'
    plt.xlabel(image_name)    
    plt.imshow(load_img(train_path + image_name)) 

#if you get a deprecated warning, you can ignore it</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1024" height="315" data-attachment-id="7123" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/cats-and-dogs-neural-networks-classification/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png" data-orig-size="1024,315" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="cats-and-dogs-neural-networks-classification" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png" src="https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png" alt="classifying cats and dogs convolutional neural networks" class="wp-image-7123" srcset="https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png 1024w, https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png 300w, https://www.relataly.com/wp-content/uploads/2022/04/cats-and-dogs-neural-networks-classification.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>I never expected to have so many pictures of cats and dogs one day, but I guess neither did you 🙂 Neural networks require a fixed input shape where each neuron corresponds to a pixel value. </p>



<p>As we can see from the sample images, the images in our dataset have different sizes and aspect ratios. For the images to fit into the input shape of our neural network, we need to put the images into a standard format. But before that, we split the data into two datasets for train and test.</p>



<h3 class="wp-block-heading" id="h-step-4-split-the-data">Step #4 Split the Data</h3>



<p>Image classification requires splitting the data into a train and a validation set. We define a split ratio of 1/5 so that 80% of the data goes into the training dataset and 20% goes into the validation dataframe. We shuffle the data to create two DataFrameswith a mix of random cat and dog pictures. In addition, we transform the classes of the images into categorical values 0-&gt;&#8221;cat&#8221; and 1-&gt;&#8221;dog&#8221;. The result is two new DataFrames: train_df (20.000 images) and validate_df (5.000 images).</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">image_df[&quot;category&quot;] = image_df[&quot;category&quot;].replace({0:'cat',1:'dog'})

train_df, validate_df = train_test_split(image_df, test_size=0.20, random_state=42)
train_df = train_df.reset_index(drop=True)
total_train = train_df.shape[0]

validate_df = validate_df.reset_index(drop=True)
total_validate = validate_df.shape[0]
train_df.head()

print(len(train_df), len(validate_df))</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Output: 20000 5000</pre></div>



<h3 class="wp-block-heading" id="h-step-5-preprocess-the-images">Step #5 Preprocess the Images</h3>



<p>The next step is to define two data generators for these DataFrames, which use the names given in the train and validation DataFrames to feed the images from the &#8220;train&#8221; path into our neural network. The data generator has various configuration options. We will perform the following operations:</p>



<ul class="wp-block-list">
<li>Rescale the image by dividing their RGB color values (1-255) by 255</li>



<li>Shuffle the images (again)</li>



<li>Bring the images into a uniform shape of 128 x 128 pixels</li>



<li>We define a batch size of 32, which processes the 32 images simultaneously.</li>



<li>The class mode is &#8220;binary&#8221; so our two prediction labels are encoded as&nbsp;float32&nbsp;scalars with values 0 or 1. As a result, we will only have a single end neuron in our network.</li>



<li>We perform some data augmentation techniques on the training data (incl. horizontal flip, shearing, and zoom). In this way, the model never sees different variants of the images, which helps to prevent overfitting.</li>
</ul>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2700" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-19-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-19.png" data-orig-size="833,262" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-19" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-19.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-19.png" alt="" class="wp-image-2700" width="753" height="236" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-19.png 833w, https://www.relataly.com/wp-content/uploads/2020/12/image-19.png 300w, https://www.relataly.com/wp-content/uploads/2020/12/image-19.png 768w" sizes="(max-width: 753px) 100vw, 753px" /><figcaption class="wp-element-caption">Some augmentation techniques</figcaption></figure>



<p>It is essential to mention that the input shape of the first layer of the neural network must correspond to the image shape of 128 x 128. The reason is that each pixel becomes an input to a neuron.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># set the dimensions to which we will convert the images
img_width, img_height = 128, 128
target_size = (img_width, img_height)
batch_size = 32
rescale=1.0/255

# configure the train data generator
print('Train data:')
train_datagen = ImageDataGenerator(rescale=rescale)
train_generator = train_datagen.flow_from_dataframe(
    train_df, 
    train_path,
    shear_range=0.2, #
    zoom_range=0.2, #
    horizontal_flip=True, # 
    shuffle=True, # shuffle the image data
    x_col='filename', y_col='category',
    classes=['dog', 'cat'],
    target_size=target_size,
    batch_size=batch_size,
    color_mode=&quot;rgb&quot;,
    class_mode='binary')

# configure test data generator
# only rescaling
print('Test data:')
validation_datagen = ImageDataGenerator(rescale=rescale)
validation_generator = validation_datagen.flow_from_dataframe(
    validate_df, 
    train_path,    
    shuffle=True,
    x_col='filename', y_col='category',
    classes=['dog', 'cat'],
    target_size=target_size,
    batch_size=batch_size,
    color_mode=&quot;rgb&quot;,
    class_mode='binary')</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Train data:
Found 20000 validated image filenames belonging to 2 classes.
Test data:
Found 5000 validated image filenames belonging to 2 classes.</pre></div>



<p>At this point, we have already completed the data preprocessing part. The next step is to define and compile the convolutional neural network.</p>



<h3 class="wp-block-heading" id="h-step-6-define-and-compile-the-convolutional-neural-network">Step #6 Define and Compile the Convolutional Neural Network</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The architecture of our image classification CNN is inspired by the famous VGGNet. In this section, we will define and compile our CNN model. We do this by defining multiple layers and stacking them on top of each other. However, to lower the amount of time needed to train the network, I reduced the number of layers.</p>



<p>The initial layer of our network is the initial input layer, which receives the preprocessed images. As already noted, the shape of the input layer needs to match the shape of our images. Considering how we have defined the format of the images in our data generators, the input shape is defined as 128 x 128 x 3. </p>



<p>The subsequent layers are four convolutional layers. Each of these layers is followed by a pooling layer. In addition, we define a Dropoutrate of 20% for each convolutional layer. </p>



<p>Finally, a fully connected output layer with 128 neurons and a binary layer for the output complete the structure of the CNN.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2608" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-8-7/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-8.png" data-orig-size="539,493" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-8" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-8.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-8.png" alt="3-dimensional Input Shape of our Neural Network " class="wp-image-2608" width="423" height="386" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-8.png 539w, https://www.relataly.com/wp-content/uploads/2020/12/image-8.png 300w" sizes="(max-width: 423px) 100vw, 423px" /><figcaption class="wp-element-caption">3-dimensional Input Shape of our Neural Network </figcaption></figure>



<p></p>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-kadence-infobox kt-info-box_4a47ba-1f"><span class="kt-blocks-info-box-link-wrap info-box-link kt-blocks-info-box-media-align-top kt-info-halign-left"><div class="kt-blocks-info-box-media-container"><div class="kt-blocks-info-box-media kt-info-media-animate-drawborder"><div class="kadence-info-box-icon-container kt-info-icon-animate-drawborder"><div class="kadence-info-box-icon-inner-container"><span class="kb-svg-icon-wrap kb-svg-icon-fe_cpu kt-info-svg-icon"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/></svg></span></div></div></div></div><div class="kt-infobox-textcontent"><h4 class="kt-blocks-info-box-title">Additional Info</h4><p class="kt-blocks-info-box-text"><strong><em>Loss function</em>:</strong> measures model accuracy during training. We try to minimize this function to &#8220;steer&#8221; the model in the right direction. We use binary_crossentropy.<br/><strong><em>Optimizer</em>:</strong> defines how the model weights are updated based on the data it sees and its loss function.<br/><strong><em>Metrics</em></strong> are<strong> </strong>used to monitor the steps during training and testing. The following example uses <em>accuracy</em>, which is the fraction of the correctly classified images.</p></div></span></div>
</div>
</div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># define the input format of the model
input_shape = (img_width, img_height, 3)
print(input_shape)

# define  model
model = Sequential()
model.add(Conv2D(32, (3, 3), strides=(1, 1), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=input_shape))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.20))
model.add(Conv2D(64, (3, 3), strides=(1, 1), activation='relu', kernel_initializer='he_uniform', padding='same'))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.20))
model.add(Conv2D(64, (3, 3), strides=(1, 1), activation='relu', kernel_initializer='he_uniform', padding='same'))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.20))
model.add(Conv2D(128, (3, 3),  strides=(1, 1),activation='relu', kernel_initializer='he_uniform', padding='same'))
model.add(MaxPooling2D((2, 2)))
model.add(Dropout(0.20))
model.add(Flatten())
model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))

# compile the model and print its architecture
opt = SGD(lr=0.001, momentum=0.9)
history = model.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])
print(model.summary())</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">input_shape: (100, 100, 3)
Model: &quot;sequential&quot;
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 100, 100, 32)      896       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 50, 50, 32)        0         
_________________________________________________________________
dropout (Dropout)            (None, 50, 50, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 50, 50, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 25, 25, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 25, 25, 64)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 25, 25, 64)        36928     
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 12, 12, 64)        0         
_________________________________________________________________
dropout_2 (Dropout)          (None, 12, 12, 64)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 12, 12, 128)       73856     
_________________________________________________________________
...
Trainable params: 720,257
Non-trainable params: 0
_________________________________________________________________
None</pre></div>



<p>At this point, we have defined and assembled our convolutional neural network. Next, it is time to train the model.</p>



<h3 class="wp-block-heading" id="h-step-7-train-the-model">Step #7 Train the Model</h3>



<p>Before we train the image classifier, we still have to choose the number of epochs. More epochs can improve the model performance and lead to longer training times. In addition, the risk increases that the model overfits. Finding the optimal number of epochs is difficult and often requires a trial-and-error approach. I typically start with a small number of 5 epochs and then increase this number until increases do not lead to significant improvements.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># train the model
epochs = 40
early_stop = EarlyStopping(monitor='loss', patience=6, verbose=1)

history = model.fit(
    train_generator,
    epochs=epochs,
    callbacks=[early_stop],
    steps_per_epoch=len(train_generator),
    verbose=1,
    validation_data=validation_generator,
    validation_steps=len(validation_generator))</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Epoch 1/35
625/625 [==============================] - 121s 194ms/step - loss: 0.7050 - accuracy: 0.5282 - val_loss: 0.6902 - val_accuracy: 0.5824
Epoch 2/35
625/625 [==============================] - 115s 183ms/step - loss: 0.6853 - accuracy: 0.5469 - val_loss: 0.6856 - val_accuracy: 0.5806
Epoch 3/35
625/625 [==============================] - 115s 184ms/step - loss: 0.6744 - accuracy: 0.5752 - val_loss: 0.6746 - val_accuracy: 0.5806
Epoch 4/35
625/625 [==============================] - 112s 180ms/step - loss: 0.6569 - accuracy: 0.5987 - val_loss: 0.6593 - val_accuracy: 0.6110
Epoch 5/35
625/625 [==============================] - 115s 185ms/step - loss: 0.6423 - accuracy: 0.6194 - val_loss: 0.6474 - val_accuracy: 0.6134
Epoch 6/35
625/625 [==============================] - 116s 185ms/step - loss: 0.6309 - accuracy: 0.6370 - val_loss: 0.6386 - val_accuracy: 0.6260
Epoch 7/35
625/625 [==============================] - 115s 183ms/step - loss: 0.6139 - accuracy: 0.6539 - val_loss: 0.6082 - val_accuracy: 0.6682</pre></div>



<p>A quick comment on the required time to train the model. Although the model is not overly complex and the size of the data is still moderate, training the model can take some time. I made two training runs &#8211; the first run on my GPU (Nvidia Geforce 3080 RTX) and the second on my CPU (AMD Ryzen 3700x). On the GPU, training took approximately 10 minutes. The CPU training was much slower and took about 30 minutes, three times longer than the GPU.  </p>



<p>After training, you may want to save the classification model and load it at a later time. You can do this with the code below:<br>However, we need to define the model strictly as it was during training before loading.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Safe the weights
model.save_weights('cats-and-dogs-weights-v1.h5')

# Define model as during training
# model architecture

# Loads the weights
model.load_weights('cats-and-dogs-weights-v1.h5')</pre></div>



<h3 class="wp-block-heading" id="h-step-8-visualize-model-performance">Step #8 Visualize Model Performance</h3>



<p>After training the model, we want to check the performance of our image classification model. For this purpose, we can apply the same performance measures as in traditional classification projects. The code below illustrates the performance of our image classifier on the validation dataset. </p>



<p>To learn more about measuring model performance, check out my <a href="https://www.relataly.com/measuring-classification-performance-with-python-and-scikit-learn/846/" target="_blank" rel="noreferrer noopener">previous post on Measuring Model Performance</a>. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">def plot_loss(history, value1, value2, title):
    fig, ax = plt.subplots(figsize=(15, 5), sharex=True)
    plt.plot(history.history[value1], 'b')
    plt.plot(history.history[value2], 'r')
    plt.title(title)
    plt.ylabel(&quot;Loss&quot;)
    plt.xlabel(&quot;Epoch&quot;)
    ax.xaxis.set_major_locator(plt.MaxNLocator(epochs))
    plt.legend([&quot;Train&quot;, &quot;Validation&quot;], loc=&quot;upper left&quot;)
    plt.grid()
    plt.show()

# plot training &amp; validation loss values
plot_loss(history, &quot;loss&quot;, &quot;val_loss&quot;, &quot;Model loss&quot;)
# plot training &amp; validation loss values
plot_loss(history, &quot;accuracy&quot;, &quot;val_accuracy&quot;, &quot;Model accuracy&quot;)
</pre></div>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="2725" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-25-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-25.png" data-orig-size="894,333" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-25" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-25.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-25.png" alt="" class="wp-image-2725" width="865" height="322" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-25.png 894w, https://www.relataly.com/wp-content/uploads/2020/12/image-25.png 300w, https://www.relataly.com/wp-content/uploads/2020/12/image-25.png 768w" sizes="(max-width: 865px) 100vw, 865px" /><figcaption class="wp-element-caption"><img decoding="async" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA34AAAFNCAYAAABfWL0+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAABxYUlEQVR4nO3deZyN5f/H8ddl7Ft2kbWItFhTiGiRSlQIbbRY2jctolX7vpNKRUmifFu0R+uvkKyhJNmyC4Nhluv3x+cMY8yMWc5yz8z7+Xicx1nu5bzPPefMnM9c131dznuPiIiIiIiIFFxFYh1AREREREREIkuFn4iIiIiISAGnwk9ERERERKSAU+EnIiIiIiJSwKnwExERERERKeBU+ImIiIiIiBRwKvxERKRQcM7Vc85551zRbKzb3zn3QzRyiYiIRIMKPxERCRzn3HLn3B7nXJV0j88JFW/1YhRNREQkX1LhJyIiQfU30Df1jnPuWKBU7OIEQ3ZaLEVERNJT4SciIkE1Drg0zf1+wNi0KzjnDnHOjXXObXDO/eOcG+6cKxJaFuece8I5t9E5tww4O4NtX3PO/eucW+2ce8A5F5edYM6595xza51zW51z3znnjk6zrJRz7slQnq3OuR+cc6VCy05yzv3knPvPObfSOdc/9Ph059yVafaxX1fTUCvnNc65P4E/Q489G9rHNufcr8659mnWj3PO3emc+8s5tz20vLZz7kXn3JPpXstHzrkbs/O6RUQk/1LhJyIiQfUzUN45d1SoIOsNvJVuneeBQ4DDgZOxQvGy0LIBQFegOdAK6Jlu2zeBJKBBaJ3OwJVkz6dAQ6AaMBt4O82yJ4CWQFugEnAbkOKcqxPa7nmgKtAMmJPN5wM4FzgBaBK6PzO0j0rAeOA951zJ0LKbsdbSs4DywOXATuw1901THFcBTgXeyUEOERHJh1T4iYhIkKW2+p0OLAZWpy5IUwwO9d5v994vB54ELgmtcgHwjPd+pfd+M/Bwmm2rA2cCN3rvd3jv1wNPA32yE8p7Pyb0nLuBe4GmoRbEIliRdYP3frX3Ptl7/1NovYuAr7z373jvE733m7z3c3JwLB723m/23u8KZXgrtI8k7/2TQAmgUWjdK4Hh3vsl3swNrTsD2IoVe4Re73Tv/boc5BARkXxI5wmIiEiQjQO+A+qTrpsnUAUoDvyT5rF/gMNCt2sCK9MtS1UXKAb865xLfaxIuvUzFCo4HwR6YS13KWnylABKAn9lsGntTB7Prv2yOeduwQq8moDHWvZSB8PJ6rneBC4GvgxdP5uHTCIikk+oxU9ERALLe/8PNsjLWcD76RZvBBKxIi5VHfa1Cv6LFUBpl6VaCewGqnjvK4Qu5b33R3NwFwLdgdOwbqb1Qo+7UKYE4IgMtluZyeMAO4DSae4fmsE6PvVG6Hy+27FWzYre+wpYS15qFZvVc70FdHfONQWOAqZksp6IiBQgKvxERCTorgBO8d7vSPug9z4ZmAg86Jwr55yri53blnoe4ETgeudcLedcReCONNv+C3wBPOmcK++cK+KcO8I5d3I28pTDisZNWLH2UJr9pgBjgKecczVDg6y0cc6VwM4DPM05d4FzrqhzrrJzrllo0znA+c650s65BqHXfLAMScAGoKhz7m6sxS/Vq8AI51xDZ45zzlUOZVyFnR84Dpic2nVUREQKNhV+IiISaN77v7z3szJZfB3WWrYM+AEb5GRMaNkrwOfAXGwAlvQthpdiXUV/B7YAk4Aa2Yg0Fus2ujq07c/plg8B5mPF1WbgUaCI934F1nJ5S+jxOUDT0DZPA3uAdVhXzLfJ2ufYQDF/hLIksH9X0KewwvcLYBvwGvtPhfEmcCxW/ImISCHgvPcHX0tEREQKDOdcB6xltF6olVJERAo4tfiJiIgUIs65YsANwKsq+kRECg8VfiIiIoWEc+4o4D+sS+szMQ0jIiJRpa6eIiIiIiIiBZxa/ERERERERAo4FX4iIiIiIiIFXNFYBwinKlWq+Hr16u29v2PHDsqUKRO7QAHKEYQMQckRhAxByRGEDMoRvAxByRGEDEHJEYQMyhG8DEHJEYQMQckRhAzKEbwM0c7x66+/bvTeVz1ggfe+wFxatmzp05o2bZoPgiDkCEIG74ORIwgZvA9GjiBk8F45gpbB+2DkCEIG74ORIwgZvFeOoGXwPhg5gpDB+2DkCEIG75UjaBm8j24OYJbPoFZSV08REREREZECToWfiIiIiIhIAafCT0REREREpIArUIO7ZCQxMZFVq1aRkJAQswyHHHIIixYtitnzhzNDyZIlqVWrFsWKFQtDKhERERERiYYCX/itWrWKcuXKUa9ePZxzMcmwfft2ypUrF5PnDmcG7z2bNm1i1apV1K9fP0zJREREREQk0gp8V8+EhAQqV64cs6KvIHHOUbly5Zi2noqIiIiISM4V+MIPUNEXRjqWIiIiIiL5T6Eo/GJl06ZNNGvWjHbt2nHooYdy2GGH0axZM5o1a8aePXuy3HbWrFlcf/31UUoqIiIiIiIFWYE/xy+WKleuzJw5c9i+fTtPPvkkZcuWZciQIXuXJyUlUbRoxj+CVq1a0apVq2hFFRERERGRAiyiLX7OuS7OuSXOuaXOuTsyWH6Ic+4j59xc59xC59xlaZYtd87Nd87Ncc7NimTOaOrfvz8333wznTp14vbbb2fGjBm0bduW5s2b07ZtW5YsWQLA9OnT6dq1KwD33nsvl19+OR07duTwww/nueeei+VLEBEREREpVBIS4J9/4Jdf4MMPYfRoWL061qlyJmItfs65OOBF4HRgFTDTOfeh9/73NKtdA/zuvT/HOVcVWOKce9t7n9oPspP3fmOkMsbKH3/8wVdffUVcXBzbtm3ju+++o2jRonz11VfceeedTJ48+YBtFi9ezLRp09i+fTuNGjXiqquu0pQKIiIiIiK5lJgIGzbA2rV2Wbdu3+3097duPXD7jz+Gww6Lfu7cimRXz9bAUu/9MgDn3ASgO5C28PNAOWcjhpQFNgNJkQp0440wZ05499msGTzzTM626dWrF3FxcQBs3bqVfv368eeff+KcIzExMcNtzj77bEqUKEGJEiWoVq0a69ato1atWnkLLyIiIiJSwCQlwapVsHw5fPFFdWbO3L+IS729MZPmpfLl4dBDoXp1OO446Nx53/1DD913u3r1qL6sPItk4XcYsDLN/VXACenWeQH4EFgDlAN6e+9TQss88IVzzgMve+9HRzBrVJUpU2bv7bvuuotOnTrxwQcfsHz5cjp27JjhNiVKlNh7Oy4ujqSkiNXHIiIiIiL78x5WroQ6dWKdhJQUK9z+/tsuy5fvf3vFCkhOTl37KABKldpXtDVoACeddGAxl3q/VKkYvbAIi2Thl9G4/z7d/TOAOcApwBHAl865773324B23vs1zrlqoccXe++/O+BJnBsIDASoXr0606dP37ssPj6eQw45hO3btwMwYkSeX1OGQrvPVHJyMrt376ZYsWIkJiaya9euvZk2bdpEpUqV2L59Oy+//DLee7Zv387OnTtJSkpi+/bte7dN3SYlJYX4+Pi997MjOTk5R+tnJSEhYb/jnBPx8fG53jZcgpAhKDmCkEE5gpchKDmCkCEoOYKQQTmClyEoOYKQISg5gpAhIjm858gnn6TmJ5/wz8UX8/dll0GRgw8Vktsc3sPWrcVYu7Yk//5bkrVrS4Zul9p7OzFx/+evVGk3NWokUL9+Am3bJlCjRgKHHppA2bKbqVWrKKVLJ5PVrGQJCVY0Ll+e47jZEoT3RiQLv1VA7TT3a2Ete2ldBjzivffAUufc30BjYIb3fg2A9369c+4DrOvoAYVfqCVwNECrVq182haz6dOnU7JkScqVKxe2F5Ub27dv39tNs1ixYpQqVWpvpjvvvJN+/foxcuRITjnlFJxzlCtXjtKlS1O0aFHKlSu3d9vUbYoUKULZsmVz9Lq2b98etuNQsmRJmjdvnqttp0+fnmmrZrQEIUNQcgQhg3IEL0NQcgQhQ1ByBCGDcgQvQ1ByBCFDUHIEIUNEctx5J3zyCTRvTt233qJuQgK8+SaULp3rHNu372ulW7bswNa7HTv2X79yZahfH044wa7r14d69ey6bl0oVaoEUAI4JIMM7XP7ysMmCO+NSBZ+M4GGzrn6wGqgD3BhunVWAKcC3zvnqgONgGXOuTJAEe/99tDtzsD9Ecwacffee2+Gj7dp04Y//vhj7/0RoWbJjh077n1zpN92wYIFkYgoIiIiIrK/J5+Ehx+GQYNg5Eh4+mkYMsQqtP/9D2rWzHCzxERYs6YkX321f3GXep3+/Lpy5ayIO+IIOO20fUVdaoEX43acAiFihZ/3Psk5dy3wORAHjPHeL3TODQ4tHwWMAN5wzs3Huobe7r3f6Jw7HPjAxnyhKDDee/9ZpLKKiIiIiEg6b7xhRV6vXvDii+Ac3HwzNGyI79uXlFatWfjwRywo1vyAlrsVKyAl5cS9uypadF8x16OHXR9++L7irlIlsuyKKXkX0QncvfdTganpHhuV5vYarDUv/XbLgKaRzCYiIiIiIvskJMCWLfDff8CH/6PRnVey9pjTmdJmHJseimPDBmvoW7bsHMom/8jEf8/hiP4ncTdv8z/O5dBDrYhr1w4uvhj27FnMmWc25vDDbdqD0KD2EiMRLfxERERERCQ6vIf4eFi/vgTz5lkBt2XL/pf0j6W9n5Bg++nAt3xOb2bSklMXvM+Om210+XLlrNWuQQOof3pTvqwyg/PHdueDP88n8b6HKT78tv2a7aZPX0vHjo2jfRgkEyr8RERERERiLDERtm2zicK3bt3/dvr7md3evt2mOoA2GT6Hc3DIIVChAlSsaJejjtr/fsP43zj3mXPYXeVwir46ldn1ylKhgq1TvHj6PR4KN0+Hyy6j+N13wLLF8PLLGa0oAaDCT0REREQiY88emD8fZs2CmTNhzRq4/35o1SrWyaJm1y473y11xMrUyz//wObN+wq3XbsOvq/ixa1wS72UL2+DoaTeTn187dolnHhio73FXMWKVriVL3+Q7pZ//AEnnQFVK1Lsxy9oWavywUOVKgXvvAONG8N999mJfpMnQ5Uq2To+Ej0q/EREREQk75KS4PffrchLLfTmzbPiD2w8/iJF4OST4d13oWvX2OYNk9279xV2n39egy++2DclwfLlNtF4WsWK2fQDdevaXOjpi7a0t9PfL1Eie5mmT/+Xjh0b5eyFrF4NnUNDb3z5JdSqlf1tnYN774Ujj4TLL7c5Fz7+OGfPLxGnwi/COnbsyA033MB5552397FnnnmGP/74g5deeinD9Z944glatWrFWWedxfjx46lQocJ+69x7772ULVuWIUOGZPq8U6ZM4cgjj6RJkyYAPPDAA5x++umcdtpp4XlhIiIiUnilpFjr0MyZNJgyBYYNg99+29dsVb68terdeKNdH3+8VTrr1lnB1707vPACXHVVLF9FtuzZAytX7l/Mpb29Zr9ZqhtRtKgVdPXqwVln7RvJsl49u9SoEcBBTjZvtqJv82aYNs0KuNy48EJ7seeeC23aUHH4cIjl3HWLFnHI3LnQoUO2Jpwv6FT4RVjfvn2ZPHnyfoXfhAkTePzxxw+67dSpUw+6TmamTJlC165d9xZ+w4cPj/lE9iIiIpIPeW/d91Jb8WbNgtmz7YQyoEbJklbcDR5s161a2egfGX3RPvRQ+PZb6NMHrr7aKqeHH475l/KdO+Gvv+yydOn+l5UrU8+bM3FxULu2FXGdO+9f2P377//Ro0cbiuanb9jx8Vah/vUXfPYZtGyZt/21aQMzZkDXrhx3++1Qtqy9N6IlIQEmTbJzDX/4geZg/2S4+mq47DLr81pI5ae3Zb7Us2dPhg0bxu7duylRogTLly9nzZo1jB8/nptuuoldu3bRs2dP7rvvvgO2rVevHrNmzaJKlSo8+OCDjB07ltq1a1O1alVahj6Ur7zyCqNHj2bPnj00aNCAcePGMWfOHD788EO+/fZbHnjgASZPnszdd9/NeeedR8+ePfn6668ZMmQISUlJHH/88YwcOZISJUpQr149+vXrx0cffURiYiLvvfcejRtrJCYREZFCZdMmK85Su2zOmmVDPoL1NWzWDC69dG9L3vdr19Lx1FMz3d2//8JPP8GPP9rpfuXLl+HQmh/Qr9X1tH7sMVb8sILl975B1VolqF7dvpdHog7cti3jwm7p0vStdtYrtUEDOOmk/eeaq1fPpiUoVizj55g+fXf+Kvr27LFJ9WbOtPPywtU6V7cu/PQTmzt3pvJVV8GiRTYRfCQPzuLFMHo0vPmmtVw2bAiPP87vW7bQ5Ntvbf7B4cNtnolrroHjjotcloDKT2/NfKly5cq0bNmSzz77jO7duzNhwgR69+7N0KFDqVSpEsnJyZx66qnMmzeP4zJ5A/76669MmDCB3377jaSkJFq0aLG38Dv//PMZMGAAYK16r732Gtdddx3dunWja9eu9OzZc799JSQk0L9/f77++muOPPJILr30UkaOHMmNN94IQJUqVZg9ezYvvfQSTzzxBK+++mrkDo6IiIgEw5498Omn9qX5449tiMmiReHYY23y7tSWvGOOObDq2bBh783kZFiwwIq8n36yy99/27KSJW3zNWvgu/VFGbnxRW6hPo//dBvLO6+mHVPYQiWKFoWqVaFatexdSpfeF2Xz5gOLutRib/36/WMfeqgVd6efbteplyOOsMFQCrzkZCvgv/gCxoyx7pnhVK4c8x94gI6ffAJPPw1//gkTJlg34HDZvdsK1pdfhu++s/fmeefBoEHQqRM4x/rp02ny4IPWFfnFF2HsWCsQO3SwAvC88zKv5AuYwlX43XgjzJkT3n02awbPPJPlKj179mTChAl7C78xY8YwceJERo8eTVJSEv/++y+///57poXf999/z3nnnUfp0G+2bt267V22YMEChg8fzn///Ud8fDxnnHFGllmWLFlC/fr1OTLUd7tfv368+OKLewu/888/H4CWLVvy/vvvZ+MAiIiISL7kvXXZfPNNG5Vx40arpK691oq95s2tWsvCtm0wc2ZFpk+3Yu/nn63nIFhh1a4dXHcdtG1ru0s7yn9SkmPTpltZMbYOJw27lOVV2jL5yk9Zmlyf9evZe/nrL7tO3W96ZcpYobhpU7vU3qd71aplxVy3bvsXd4cfbnPSFVre28/53Xfh8cetC2QkxMXBU09Bo0ZWZLVtCx99ZM2nefHHH1a8vfGGtVAffjg88oi9jmrVMt6meXN49VV47DF4/XUrAnv3hpo1rVAcONDetAVY4Sr8YqRr164MGzaM2bNns2vXLipWrMgTTzzBzJkzqVixIv379ychdcbMTLg0k2Gm1b9/f6ZMmULTpk154403mD59epb78d5nubxEaLiouLg4kpKSslxXRERE8qE1a+Ctt6zlY+FCq8a6d4d+/eCMMzLtjue9td6ldtv86Sfruul9U4oUscbBSy+17/bt2llvv0y+vgD2NNWrA7f2hhNrUr57dy57+URrcTz++APW37nTGhfXrWO/wjD1sn37ejp0OGy/4q5UqTAds4Lmnntg1Ci4/XbIYrDAsBk0yJpSe/WyET+nTLE3Sk7s2QMffGCte9Om2Ruoe3fb96mnZr9/cKVKcMst1iD02Wd2/t8998ADD0DPnlYQt2mT9Zs3nypchd9BWuYipWzZsnTs2JHLL7+cvn37sm3bNsqUKcMhhxzCunXr+PTTT+mYRZ/qDh060L9/f+644w6SkpL46KOPGDRoEADbt2+nRo0aJCYm8vbbb3PYYYcBUK5cOban/7cX0LhxY5YvX87SpUv3nhN48sknR+R1i4iISEDs3An/+5+17n35pY1W0qaNffm/4IIM+zbu3m2949J220ydmqBcOdv8/POhbNm5DBjQNG89+Nq3tyc46yw7z+ydd6yZLo3SpfdNg5CR6dP/pGPHw/IQopB49lkYMQKuuMIG1omW006zJuGuXa0b5pgxcNFFB99u6VJ45RVrpduwwU60fOgha93LSwtdXBycfbZd/vwTRo60TO+8Y62D11wDffvu35c4nytchV8M9e3bl/PPP58JEybQuHFjmjdvztFHH83hhx9Ou3btsty2RYsW9O7dm2bNmlG3bl3at2+/d9mIESM44YQTqFu3Lscee+zeYq9Pnz4MGDCA5557jkmTJu1dv2TJkrz++uv06tVr7+Aug6M50pKIiIhEh/fwww+kvP4mbtJ7uO3b2FOjDusvHco/HS5l3SFHsn07bH/bBuhMe1m61MZ02b3bdnX44fa9vV07a6g5+uh9UxJMn74lPKdtNW4M//d/cM45dt7Vc8/Zl28Jn7fespau88+3oj/arVqNGlnx16OHDbKyeLFN+p6+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Ctavt/E8Ro+2eaKrVIFbboEBA2xQyANs3gy33gpt27K2SxcaRz2x5Fnp0lbdL1sGjRrFOo2IFHIq/ERERCLEexvlf9QomDzZRurs0AEeeADOP9+mXMjUnXfCli0wcqQVgZI/FSumok9EAkGFn4iISJht3my9NF9+GRYvtlO+rr7aTu9q0iQbO/jlF2savPFGOO44GylSREQkD1T4iYiIhIH38PPP1ro3caJN2Xbiida984ILrNdftiQn27lgNWrAvfdGMrKIiBQiKvxERETSSUy0VrvNm2H+/PJs22a3N23a93jq7bTXO3ZA2bLQvz8MGgTNmuXiyV96CX77zeaAK18+zK9MREQKKxV+IiJS4HlvhdnSpfDXXzbQSkbFW+rt7dvTbt1iv33FxUGlSnapXBlq1bLemJUrWzfO3r1tQMdc+fdfGD7chvns1Su3L1dEROQAKvxERKRA8B42bLDibulS+PPPfbeXLoX//tt//SJFoGJFK9gqVbKelUcfva+gS71euXIunTo13ftY+fIRnEpvyBDrI/rii5qvT0REwkqFn4iI5BveW2td2qIu7e1t2/atW6QI1KtnUyVceCE0bGi3jzjCirzy5W2dg5k+fQutWkXsJe3zzTcwfjzcfbeFFRERCSMVfiIiEjgJCTB//iEsW3ZggRcfv2+9uDgr7ho2hLZtrbBLLfDq1YPixWP1CnJozx4b9vPww+GOO2KdRkRECiAVfiIiEgjx8TB1Krz/PnzyCcTHNwegaFGoX9+KuQ4d7Dq1wKtb16ZJy/eefBKWLLEDUKpUrNOIiEgBpMJPRERiZssW+Ogjm9z8889h926oVs26ZtatO5/evY+lbl0r/gqs5cthxAib0f3MM2OdRkRECqiC/KdUREQCaN06mDLFWva++QaSkqB2bRg82Gqfdu2sC+f06Zs44ohYp42CG26wkw2feSbWSUREpABT4SciIhG3cqUVepMnww8/2CAtDRrALbdAjx7QqlW6QSw/+YTDx42DNm2gRImY5Y64Dz+0y2OPWfUrIiISISr8REQkIv780wq999+HmTPtsWOPhXvusZa9Y47JYMaCtWutBWziROqAzafw4otRTh4lO3fC9dfb5H833hjrNCIiUsCp8BMRkbDwHubP39eyt2CBPX788fDII1bsZTpLQUoKvPoq3HabDek5YgQr586l9ksvWd/P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uO02eOUVGDYMbr01y9XffBP69oUTT7TunZUqRSmniIiIiEgBocJPou/hh+GJJ+Caa2DEiCxXHTkS+ve30+w++wzKl49ORBERERGRgkSFn0TVYR98YK18F18Mzz1no2hm4skn4eqrbcDPjz6CMmWiGFREREREpABR4SfRM24cDZ97Drp3h9dfhyIZv/28h/vvhyFD4IILYPJkKFkyyllFRERERAoQFX4SHT/9BJdfzpbmzWHCBCia8Uwi3sMdd8A990C/fjbDgwbVFBERERHJGxV+EnkbN0Lv3lCnDgvvvz/T5ruUFJsr/bHHbMDPMWMgLi7KWUVERERECiBN4C6RlZICl1wC69fD//0fSdu2ZbhacjIMGGA9QIcMseIvi9P/REREREQkB9TiJ5H16KM2HOczz0CLFhmukpgIF11kRd8996joExEREREJN7X4SeR8+y0MHw59+sDgwRmukpBgvUA//NAKvoNM6SciIiIiIrmgwk8iY906K/gaNIDRozNswtu5E849F778El54wab1ExERERGR8FPhJ+GXnAwXXgj//QdffAHlyh2wyvbt0LUr/PCDDeJy2WXRjykiIiIiUlio8JPwu/9++OYbq+iOPfaAxVu2QJcuMHu2TdfQu3cMMoqIiIiIFCIq/CS8vvwSRoywSfgyaMbbsqUYnTrBokU2MXu3bjHIKCIiIiJSyKjwk/BZvdqG52zSBF58McPFN97YjA0b4KOPoHPnGGQUERERESmENJ2DhEdSkg3msnMnvPcelCmzd5H3Nmpn27awYUMJPvtMRZ+IiIiISDSp8JPwGD7cRmoZPRqOOmrvw4sXw5lnQvfuULYsPP30XDp0iGFOEREREZFCSIWf5N0nn9hE7YMG2WiewNatcMstNrbLzz/b/O1z5kCjRttjGlVEREREpDDSOX6SN//8A5dcAs2awTPPkJICb74Jd9wBGzbAlVfCgw9C1aqxDioiIiIiUnip8JPc27PH5mJISoL33uOXuSW5/nqYMQPatIGpU6Fly1iHFBERERERdfWU3Lv9dvjlF7Y8NYbLHmzAiSfCypUwbhz8+KOKPhERERGRoFCLn+TO++/DM88w+6Tr6XhzTxISrA4cNgzKlYt1OBERERERSUuFn+TcX3+ReOnl/F6yNSf+8Didz4ann4aGDWMdTEREREREMqLCT3Lkr4UJ+HYXUHmH48Z67/LBC8U5++xYpxIRERERkazoHD/Jlvh4uPNO+Oq4m2mwdTbT+r3J50vqqegTEREREckHIlr4Oee6OOeWOOeWOufuyGSdjs65Oc65hc65b3OyrUSe9zB+PDRqBH8//A6DUkYSf9WtnP9GN4oXj3U6ERERERHJjogVfs65OOBF4EygCdDXOdck3ToVgJeAbt77o4Fe2d1WIu+336B9e7joIjix4hLeKj0Q2rWj7LMPxjqaiIiIiIjkQCRb/FoDS733y7z3e4AJQPd061wIvO+9XwHgvV+fg20lQjZsgEGDbDqGP/6A11/cyaQivYgrXRImTIBixWIdUUREREREcsB57yOzY+d6Al2891eG7l8CnOC9vzbNOs8AxYCjgXLAs977sdnZNs0+BgIDAapXr95ywoQJe5fFx8dTtmzZiLy+nAhCjuxmmDPnEO666xh27izK+eevol+/f2j50kMc+tlnzHvkEba0bh2VHJEUhAxByRGEDMoRvAxByRGEDEHJEYQMyhG8DEHJEYQMQckRhAzKEbwM0c7RqVOnX733rQ5Y4L2PyAXrtvlqmvuXAM+nW+cF4GegDFAF+BM4MjvbZnRp2bKlT2vatGk+CIKQIzsZVqzwvkoV7xs18n7hwtCDr7/uPXg/fHjUckRaEDJ4H4wcQcjgvXIELYP3wcgRhAzeByNHEDJ4rxxBy+B9MHIEIYP3wcgRhAzeK0fQMngf3RzALJ9BrRTJ6RxWAbXT3K8FrMlgnY3e+x3ADufcd0DTbG4rYZSQAD16wO7dMGUKNG4MLFgAV18NnTrBvffGOKGIiIiIiORWJM/xmwk0dM7Vd84VB/oAH6Zb539Ae+dcUedcaeAEYFE2t5Uwuv56mDkT3nwzVPTFx0OvXlC+vA3rGRcX64giIiIiIpJLEWvx894nOeeuBT4H4oAx3vuFzrnBoeWjvPeLnHOfAfOAFKx75wKAjLaNVNbC7pVX7DJ0KJx3HjaHw6BBNrLLV1/BoYfGOqKIiIiIiORBJLt64r2fCkxN99iodPcfBx7PzrYSfjNmwLXXwumnw4gRoQdfecVa+UaMsG6eIiIiIiKSr0V0AncJtvXr7by+GjXgnXdCvTkXLLB+n2ecAXfeGeuIIiIiIiISBhFt8ZPgSkqC3r1h40b48UeoXDm04J57oGRJGDcOiuj/AiIiIiIiBYG+2RdSd9wB06fDqFHQokXowd9/h/ffh+uug6pVYxlPRERERETCSIVfITRxIjz5JFxzDfTrl2bBww9D6dJwww0xyyYiIiIiIuGnwq+QWbAALr8c2raFp55Ks2DZMjvRb/BgqFIlZvlERERERCT8VPgVIv/9B+efD+XKwXvvQfHiaRY+9piN7nLLLbGKJyIiIiIiEaLBXQqJlBS49FL4+2/45huoWTPNwtWr4fXX4bLL0i0QEREREZGCQIVfIfHWW3X56CN47jlo3z7dwqeeguRkuP32mGQTEREREZHIUlfPQuDTT+GNN+px8cU2Wft+Nm60oT0vvBDq149JPhERERERiSwVfgXcX39ZTXf44Tt4+WVwLt0Kzz4LO3fa/A4iIiIiIlIgqfArwHbutMFcnIP7719A6dLpVti2DZ5/3lZq0iQmGUVEREREJPJ0jl8B5T0MGADz58PUqVCyZMKBK730EmzdCnfeGf2AIiIiIiISNWrxK6Cefx7Gj4f774cuXTJYYedOG9TljDOgZcuo5xMRERERkehR4VcAffedTcfXrVsWjXmvvQYbNsCwYVHNJiIiIiIi0afCr4BZvRouuMAG6Bw7Fopk9BPes8cmbG/fPoO5HUREREREpKDROX4FyJ490LMnxMfD11/DIYdksuK4cbBqFbzySlTziYiIiIhIbKjwK0Buugl+/hkmToSjj85kpeRkeOQRO6/vjDOimk9ERERERGJDhV8B8cYbNkjnrbdCr15ZrPjee7B0KUyenMGkfiIiIiIiUhDpHL8CYPZsGDwYTjkFHnooixVTUmyFo46Cc8+NVjwREREREYkxtfjlcxs32vzr1arBhAlQNKuf6Mcf28R+mY76IiIiIiIiBZEKv3wsORn69oV//4UffoCqVbNY2Xt48EEb7rNv36hlFBERERGR2FPhl48NHw5ffQWvvgrHH5/1uhVmz4YZM2DUqIM0C4qIiIiISEGj/n751Pvv2+CcAwfCFVccfP26b78NNWpAv36RDyciIiIiIoGipp98aOFCq99at4bnnsvGBv/3f1T87Td48kkoWTLi+UREREREJFjU4pfPbN4M3btD2bLW6leiRDY2eughEsuXt+ZBEREREREpdFT45SNJSTYuy4oVNg3fYYdlY6O5c+Hjj1nVo4dViyIiIiIiUuhEtPBzznVxzi1xzi11zt2RwfKOzrmtzrk5ocvdaZYtd87NDz0+K5I584uhQ+GLL2yi9rZts7nRww9DuXKsPu+8iGYTEREREZHgitg5fs65OOBF4HRgFTDTOfeh9/73dKt+773vmsluOnnvN0YqY37y9tvwxBNw9dVw5ZXZ3OiPP2DiRLj9dpLKlYtoPhERERERCa5Itvi1BpZ675d57/cAE4DuEXy+AuvXX63Y69ABnnkmBxs+8oidBHjjjRFKJiIiIiIi+UEkC7/DgJVp7q8KPZZeG+fcXOfcp865o9M87oEvnHO/OucK7agk69fDeedBtWrw3ntQrFg2N1yxAsaNgwEDoHr1iGYUEREREZFgc977yOzYuV7AGd77K0P3LwFae++vS7NOeSDFex/vnDsLeNZ73zC0rKb3fo1zrhrwJXCd9/67DJ5nIDAQoHr16i0nTJiwd1l8fDxlAzCgSW5zJCY6hgxpypIl5Xj++d9o2DA+29s2eO45an70Eb+8/Ta7q1XL98eioGUISo4gZFCO4GUISo4gZAhKjiBkUI7gZQhKjiBkCEqOIGRQjuBliHaOTp06/eq9b3XAAu99RC5AG+DzNPeHAkMPss1yoEoGj98LDDnYc7Zs2dKnNW3aNB8Euc1x1VXeg/fjx+dww7VrvS9Z0vsrrshzhnALQo4gZPA+GDmCkMF75QhaBu+DkSMIGbwPRo4gZPBeOYKWwftg5AhCBu+DkSMIGbxXjqBl8D66OYBZPoNaKZJdPWcCDZ1z9Z1zxYE+wIdpV3DOHeqcc6HbrbGup5ucc2Wcc+VCj5cBOgMLIpg1cF55BUaOhNtusykccuTpp2HPHrj99ohkExERERGR/CVio3p675Occ9cCnwNxwBjv/ULn3ODQ8lFAT+Aq51wSsAvo4733zrnqwAehmrAoMN57/1mksgbNjz/CNddAly7w0EM53HjLFpvv4YILoGHDiOQTEREREZH8JWKFH4D3fiowNd1jo9LcfgF4IYPtlgFNI5ktqFatgh49oG5dGD8e4uJyuIPnn4ft223SPxERERERESJc+EnO7NplI3ju2AHffAMVK+ZwB/Hx8OyzcM45cNxxEckoIiIiIiL5jwq/gPAeBg2CWbNgyhRo0iQXO3n5Zdi8GYYNC3c8ERERERHJxyI5uIvkwDPP2LR7990H3XMzzX1CAjzxBJx6KpxwQrjjiYiIiIhIPqYWvwD46isYMsS6eQ4fnsudvP46rF0Lb78d1mwiIiIiIpL/qcUvxpYtg9694aij4M03oUhufiKJifDYY3DiidCpU9gzioiIiIhI/qYWvxiKj7dund7D//4H5crlckfvvAPLl9uInjYFhoiIiIiIyF4q/GLEe+jfH37/HT77DI44Ipc7SkmBhx+2UTzPPjucEUVEREREpIBQ4RcjDz4IkyfDk0/C6afnYUcffACLF8OECWrtExERERGRDOkcvxj46CO46y64+GK46aY87Mh7qyCPPBJ69gxbPhERERERKVjU4hdlixbBRRdBq1YwenQeG+k++wx++w3GjIG4uLBlFBERERGRgkUtflH03382mEupUvD++3adJw89BLVrWyUpIiIiIiKSCbX4RUlyMlx4oQ2++c03Vq/lyXffwQ8/2EiexYuHI6KIiIiIiBRQavGLkuHD4dNPrU476aQ87uzzz+GKK6BaNbsWERERERHJggq/KPjmm6o88ggMGmSXXFu0CM46C7p0sYFdJkwIQ39REREREREp6FT4RdicOfDYY4056SR47rlc7mTTJrjuOjj2WPjpJ3jiCVi4EDp1CmdUEREREREpoHSOXwRt2ADnngvlyycyaVJczk/F27MHXnwR7r8ftm2DwYPh3nuhatUIpBURERERkYJKhV8E7doFNWpA//4LqV69ZfY39B4+/hhuuQX+/BM6d4annoKjj45cWBERERERKbDU1TOC6tSxnpmNGm3P/kbz5sHpp0O3bjY33yef2Hx9KvpERERERCSXVPhFWLYnaF+3DgYOhObNbVL255+3IvCss/I4y7uIiIiIiBR26uoZawkJ8Oyz8OCD1jf0+uvhrrugUqVYJxMRERERkQJChV+seA+TJ8Ntt8Hff8M558Djj0OjRrFOJiIiIiIiBYy6esbCr7/CySdDr15Qpgx8+SV8+KGKPhERERERiQgVftG0Zg307w/HHw+LF8OoUXY+32mnxTqZiIiIiIgUYOrqGQVFEhJgxAh45BFISoJbb4U774RDDol1NBERERERKQRU+EVSSgq88w6tb7rJZnPv0QMeewwOPzzWyUREREREpBBR4RdJ8+bBxReT2LAhJSdNgg4dYp1IREREREQKIRV+kdSsGUybxq8pKXRU0SciIiIiIjES0cFdnHNdnHNLnHNLnXN3ZLC8o3Nuq3NuTuhyd3a3zTc6doQiGkNHRERERERi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"></figcaption></figure>



<p>Next, let&#8217;s print the accuracy and a confusion matrix on the predictions from the validation dataset.  </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># function that returns the label for a given probability
def getLabel(prob):
    if(prob &gt; .5):
               return 'dog'
    else:
               return 'cat'

# get the predictions for the validation data
val_df = validate_df.copy()
val_df['pred'] = &quot;&quot;
val_pred_prob = model.predict(validation_generator)

for i in range(val_pred_prob.shape[0]):
    val_df['pred'][i] = getLabel(val_pred_prob[i])
          
# create a confusion matrix
y_val = val_df['category']
y_pred = val_df['pred']

print('Accuracy: {:.2f}'.format(accuracy_score(y_val, y_pred)))
cnf_matrix = confusion_matrix(y_val, y_pred)

# plot the confusion matrix in form of a heatmap

%matplotlib inline
class_names=[False, True] # name  of classes
fig, ax = plt.subplots(figsize=(8, 8))
tick_marks = np.arange(len(class_names))
plt.xticks(tick_marks, class_names)
plt.yticks(tick_marks, class_names)
sns.heatmap(pd.DataFrame(cnf_matrix), annot=True, cmap=&quot;YlGnBu&quot;, fmt='g')
plt.title('Confusion matrix')
plt.ylabel('Actual label')
plt.xlabel('Predicted label')</pre></div>



<pre class="wp-block-preformatted">Accuracy: 0.82</pre>



<figure class="wp-block-image size-large"><img decoding="async" width="479" height="496" data-attachment-id="2716" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/image-24-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/12/image-24.png" data-orig-size="479,496" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-24" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/12/image-24.png" src="https://www.relataly.com/wp-content/uploads/2020/12/image-24.png" alt="confusion matrix for an image classification model" class="wp-image-2716" srcset="https://www.relataly.com/wp-content/uploads/2020/12/image-24.png 479w, https://www.relataly.com/wp-content/uploads/2020/12/image-24.png 290w" sizes="(max-width: 479px) 100vw, 479px" /></figure>



<h3 class="wp-block-heading" id="h-step-9-image-classification-on-sample-images">Step #9 Image Classification on Sample Images</h3>



<p>Now that we have trained the model, I bet you can&#8217;t wait to test the image classifier on some sample data. For this purpose, ensure that you have some sample images in the &#8220;sample&#8221; folder. Running the code below will feed the image classifier with the test dataset. Based on this dataset, the model will then predict the labels for the images from the sample folder. Finally, the code below prints the images in an image grid and the predicted labels. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># set the path to the sample images
sample_path = &quot;data/images/cats-and-dogs/sample/&quot;
sample_df = createImageDf(sample_path)
sample_df['category'] = sample_df['category'].replace({0:'cat',1:'dog'})
sample_df['pred'] = &quot;&quot;

# create an image data generator for the sample images - we will only rescale the images
test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_dataframe(
    sample_df, 
    sample_path,    
    shuffle=False,
    x_col='filename', y_col='category',
    target_size=target_size)

# make the predictions 
pred_prob = model.predict(test_generator)
image_number = pred_prob.shape[0]

# define the plot size
for i in range(pred_prob.shape[0]):
    sample_df['pred'][i] = getLabel(pred_prob[i])
    
print('Accuracy: {:.2f}'.format(accuracy_score(sample_df['category'], sample_df['pred'])))

nrows = 6
ncols = int(round(image_number / nrows, 0))
fig, axs = plt.subplots(nrows, ncols, figsize=(15, 15))
for i, ax in enumerate(fig.axes):
    if i &lt; sample_df.shape[0]:
        filepath = sample_path + sample_df.at[i ,'filename']
        ax = ax
        img = Image.open(filepath).resize(target_size)
        ax.imshow(img)
        ax.set_title(sample_df.at[i ,'filename'] + '\n' + ' predicted: '  + str(sample_df.at[i ,'pred']))
        result = [True if sample_df.at[i ,'pred'] == sample_df.at[i ,'category'] else False]
        ax.set_xlabel(str(result))
        ax.set_xticks([]); ax.set_yticks([])</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="862" height="864" data-attachment-id="6516" data-permalink="https://www.relataly.com/image-classification-with-deep-learning/2485/output-5/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/output.png" data-orig-size="862,864" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="output" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/output.png" src="https://www.relataly.com/wp-content/uploads/2022/04/output.png" alt="image classification - the image shows several dogs and cats" class="wp-image-6516" srcset="https://www.relataly.com/wp-content/uploads/2022/04/output.png 862w, https://www.relataly.com/wp-content/uploads/2022/04/output.png 300w, https://www.relataly.com/wp-content/uploads/2022/04/output.png 150w, https://www.relataly.com/wp-content/uploads/2022/04/output.png 768w" sizes="(max-width: 862px) 100vw, 862px" /></figure>



<p>Our image classifier achieves an accuracy of around 83% on the validation set. The model is not perfect, but it should have labeled most images correctly. With deeper architectures, more data, and training runs, you can create classification models that achieve better results over 95%.</p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>In this tutorial, you learned how to train an image classification model. We have prepared a dataset and performed several transformations to bring the data in shape for training. Finally, we have trained a convolutional neural network to distinguish between dogs and cats. You can now use this knowledge to train image classification models that determine other objects. </p>



<p>There are many other cool things that you can do with CNNs. For example, object localization in images and videos and even stock market prediction. But these are topics for further articles.</p>



<p>I am always happy to receive feedback. I hope you enjoyed the article and would be happy if you left a comment. Cheers</p>



<h2 class="wp-block-heading" id="h-sources-and-further-reading">Sources and Further Reading</h2>



<ol class="wp-block-list"><li><a href="https://amzn.to/3MAy8j5" target="_blank" rel="noreferrer noopener">Andriy Burkov Machine Learning Engineering</a></li><li><a href="https://amzn.to/3D0gB0e" target="_blank" rel="noreferrer noopener">Oliver Theobald (2020) Machine Learning For Absolute Beginners: A Plain English Introduction</a></li><li><a href="https://amzn.to/3MyU6Tj" target="_blank" rel="noreferrer noopener">Charu C. Aggarwal (2018) Neural Networks and Deep Learning</a></li><li><a href="https://amzn.to/3S9Nfkl" target="_blank" rel="noreferrer noopener">Aurélien Géron (2019) Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems </a></li><li><a href="https://amzn.to/3EKidwE" target="_blank" rel="noreferrer noopener">David Forsyth (2019) Applied Machine Learning Springer</a></li><li>[1] D. H. Hubel and T. N. Wiesel &#8211; Receptive Fields of Neurons in the Cat&#8217;s Striate Cortex, The Journal of physiology (1959)</li></ol>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>
<p>The post <a href="https://www.relataly.com/image-classification-with-deep-learning/2485/">Image Classification with Convolutional Neural Networks &#8211; Classifying Cats and Dogs in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/image-classification-with-deep-learning/2485/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2485</post-id>	</item>
		<item>
		<title>Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques</title>
		<link>https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/</link>
					<comments>https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/#comments</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Mon, 29 Jun 2020 21:47:28 +0000</pubDate>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Feature Engineering]]></category>
		<category><![CDATA[Finance]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Stock Market Forecasting]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Time Series Forecasting]]></category>
		<category><![CDATA[Use Cases]]></category>
		<category><![CDATA[Yahoo Finance API]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Feature Engineering for Time Series Forecasting]]></category>
		<category><![CDATA[Intermediate Tutorials]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=1813</guid>

					<description><![CDATA[<p>Are you interested in learning how multivariate forecasting models can enhance the accuracy of stock market predictions? Look no further! While traditional time series data provides valuable insights into historical trends, multivariate forecasting models utilize additional features to identify patterns and predict future price movements. This process, known as &#8220;feature engineering,&#8221; is a crucial step ... <a title="Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques" class="read-more" href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/" aria-label="Read more about Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques">Read more</a></p>
<p>The post <a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/">Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:60%">
<p>Are you interested in learning how multivariate forecasting models can enhance the accuracy of stock market predictions? Look no further! While traditional time series data provides valuable insights into historical trends, multivariate forecasting models utilize additional features to identify patterns and predict future price movements. This process, known as &#8220;feature engineering,&#8221; is a crucial step in creating accurate stock market forecasts.</p>



<p>In this article, we dive into the world of feature engineering and demonstrate how it can improve stock market predictions. We explore popular financial analysis metrics, including Bollinger bands, RSI, and Moving Averages, and show how they can be used to create powerful forecasting models.</p>



<p>But we don&#8217;t just stop at theory. We provide a hands-on tutorial using Python to prepare and analyze time-series data for stock market forecasting. We leverage the power of recurrent neural networks with LSTM layers, based on the Keras library, to train and test different model variations with various feature combinations.</p>



<p>By the end of this article, you&#8217;ll have a thorough understanding of feature engineering and how it can improve the accuracy of stock market predictions. So, buckle up and get ready to discover how multivariate forecasting models can take your stock market analysis to the next level!</p>



<p><strong>New to time series modeling?</strong><br>Consider starting with the following tutorial on univariate time series models: <a href="https://www.relataly.com/stock-market-prediction-with-multivariate-time-series-in-python/1815/" target="_blank" rel="noreferrer noopener">Stock-market forecasting using Keras Recurrent Neural Networks and Python</a>. </p>



<p class="has-accent-color has-blush-light-purple-gradient-background has-text-color has-background"><strong>Disclaimer</strong>: This article does not constitute financial advice. Stock markets can be very volatile and are generally difficult to predict. Predictive models and other forms of analytics applied in this article only illustrate machine learning use cases.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-full"><img decoding="async" width="503" height="503" data-attachment-id="13113" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/multivariate-engineering-for-time-series-analysis-in-python-min/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png" data-orig-size="503,503" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="multivariate-engineering-for-time-series-analysis-in-python-min" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png" src="https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png" alt="A cartoon-style illustration of a cute animal, possibly a raccoon, sitting at a desk and working on a laptop. The animal is wearing glasses and appears to be focused on a screen displaying graphs and charts related to time series analysis. In the background, there are books, a clock, and other office supplies. This image represents the concept of feature engineering, a process of selecting and transforming data features to improve machine learning models, particularly for time series data. Midjourney" class="wp-image-13113" srcset="https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png 503w, https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png 300w, https://www.relataly.com/wp-content/uploads/2023/03/multivariate-engineering-for-time-series-analysis-in-python-min.png 140w" sizes="(max-width: 503px) 100vw, 503px" /><figcaption class="wp-element-caption">Squirrels mastered the art of multivariate feature engineering for time series analysis a long time ago. You can do it too! Image generated with <a href="http://www.midjourney.com" target="_blank" rel="noreferrer noopener">Midjourney</a></figcaption></figure>
</div>
</div>



<h2 class="wp-block-heading" id="h-feature-engineering-for-stock-market-forecasting-borrowing-features-from-chart-analysis">Feature Engineering for Stock Market Forecasting &#8211; Borrowing Features from Chart Analysis</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The idea behind multivariate time series models is to feed the model with additional features that improve prediction quality. An example of such an additional feature is a &#8220;moving average.&#8221; Adding more features does not automatically improve predictive performance but increases the time needed to train the models. The challenge is to find the right combination of features and to create an input form that allows the model to recognize meaningful patterns. There is no way around conducting experiments and trying out feature combinations. This process of trial and error can be time-consuming. It is, therefore, helping to build upon established indicators.</p>



<p>In stock market forecasting, we can use indicators from chart analysis. This domain forecasts future prices by studying historical prices and trading volume. The underlying idea is that specific patterns or chart formations in the data can signal the timing of beneficial buying or selling decisions. We can borrow indicators from this discipline and use them as input features. </p>



<p>When we develop predictive machine learning models, the difference from chart analysis is that we do not aim to analyze the chart ourselves manually, but try to create a machine learning model, for example, a recurrent neural network, that does the job for us. </p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image is-resized"><img decoding="async" src="https://www.relataly.com/wp-content/uploads/2020/06/image-28.png" alt="Feature engineering for multivariate stock market prediction - A multivariate time series forecast. Keras, Scikit-Learn, Python, Tutorial" width="383" height="200"/><figcaption class="wp-element-caption">A multivariate time-series forecast, as we will create it in this article. Exemplary chart with technical indicators (Bollinger bands, RSI, and Double-EMA)</figcaption></figure>
</div>
</div>



<h2 class="wp-block-heading" id="h-stock-market-forecasting-does-this-really-work">Stock Market Forecasting &#8211; Does this really Work?</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>It is essential to point out that the effectiveness of chart analysis and algorithmic trading is controversial. There is at least as much controversy about whether it is possible to predict the price of stock markets with neural networks. Various studies and researchers have examined the effectiveness of chart analysis with different results. One of the most significant points of criticism is that it cannot take external events into account. Nevertheless, many financial analysts consider financial indicators when making investment decisions, so a lot of money is moved simply because many people believe in statistical indicators. </p>



<p>So without knowing how well this will work, it is worth an attempt to feed a neural network with different financial indicators. But first and foremost, I see this as an excellent way to show how feature engineering works. Just make sure not to rely on the predictions of these models blindly. </p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<p>Also: <a href="https://www.relataly.com/univariate-stock-market-forecasting-using-a-recurrent-neural-network/122/" target="_blank" rel="noreferrer noopener">Stock Market Prediction using Univariate Recurrent Neural Networks</a></p>



<h2 class="wp-block-heading" id="h-selected-statistical-indicators">Selected Statistical Indicators</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The following indicators are commonly used in chart analysis and may be helpful when creating forecasting models:</p>



<ul class="wp-block-list">
<li>Relative Strength Index</li>



<li>Simple Moving Averages</li>



<li>Exponential Moving Averages</li>



<li>Bolliger Bands</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-relative-strength-index-rsi">Relative Strength Index (RSI)</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The Relative Strength Index (RSI) is one of the most commonly used oscillating indicators. In 1978, Welles Wilder developed it to determine the momentum of price movements and compare the strength of price losses in a period with price gains. It can take percentage values between 0 and 100. </p>



<p>Reference lines determine how long an existing trend will last before expecting a trend reversal. In other words, when the price is heavily oversold or overbought, one should expect a trend reversal.</p>



<ul class="wp-block-list">
<li>The reference line is at 40% (oversold) and 80% (overbought) with an upward trend.</li>



<li>The reference line is at 20% (oversold) and 60% (overbought) with a downtrend.</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<p>The formula for the RSI is as follows:</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:25.5%">
<ul class="wp-block-list">
<li>Calculate the sum of all positive and negative price changes in a period (e.g., 30 days):</li>



<li>We then calculate the mean value of the sums with the following formula:</li>



<li>Finally, we calculate the RSI with the following formula:</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:55.9%">
<figure class="wp-block-image is-resized"><img decoding="async" src="https://wikimedia.org/api/rest_v1/media/math/render/svg/4fc95bb85e82212cece770cb561766b2f4b2b579" alt="feature engineering for stock price prediction:  formula for the rsi, Keras, Scikit-Learn, Python, Tutorial" width="321" height="84"/></figure>



<figure class="wp-block-image is-resized"><img decoding="async" src="https://wikimedia.org/api/rest_v1/media/math/render/svg/91d80c46471846096df7dec9be671572c7b7e064" alt="feature engineering for stock price prediction: formula for the rsi, Keras, Scikit-Learn, Python, Tutorial" width="144" height="68"/></figure>



<figure class="wp-block-image is-resized"><img decoding="async" src="https://wikimedia.org/api/rest_v1/media/math/render/svg/bd24e0da167456b367d13ec0327eca724feecc58" alt="feature engineering for stock price prediction:  formula for the rsi,Keras, Scikit-Learn, Python, Tutorial" width="148" height="36"/></figure>



<p></p>
</div>
</div>



<h3 class="wp-block-heading" id="h-simple-moving-averages-sma">Simple Moving Averages (SMA)</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Simple Moving Averages (SMA) is another technical indicator that financial analysts use to determine if a price trend will continue or reverse. The SMA is the average sum of all values within a certain period. Financial analysts pay close attention to the 200-day SMA (SMA-200). When the price crosses the SMA, this may signal a trend reversal. Furthermore, we often use SMAs for 50 (SMA-50) and 100 days (SMA-100) periods. In this regard, two popular trading patterns include the death cross and a golden cross. </p>



<ul class="wp-block-list">
<li>A&nbsp;death cross&nbsp;occurs when the trend line of the SMA-50/100 crosses below the 200-day SMA.&nbsp;This suggests that a falling trend will likely accelerate downwards.</li>



<li>A golden cross occurs when the trend line of the SMA-50/100 crosses over the 200-day SMA, suggesting a rising trend will likely accelerate upwards.</li>
</ul>



<p>We can use the SMA in the input shape of our model simply by measuring the distance between two trendlines.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-exponential-moving-averages-ema">Exponential Moving Averages (EMA)</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The exponential moving average (EMA) is another lagging trend indicator. Like the SMA, the EMA measures the strength of a price trend. The difference between SMA and EMA is that the SMA assigns equal values to all price points, while the EMA uses a multiplier that weights recent prices higher.</p>



<p>The formula for the EMA is as follows: Calculating the EMA for a given data point requires past price values. For example, to calculate the SMA for today, based on 30 past values, we calculate the average price values for the past 30 days. We then multiply the result by a weighting factor that weighs the EMA. The formula for this multiplier is as follows: Smoothing factor / (1+ days)</p>



<p>It is common to use different smoothing factors. For a 30-day moving average, the multiplier would be [2/(30+1)]= 0.064. </p>



<p>As soon as we have calculated the EMA for the first data point, we can use the following formula to calculate the ema for all subsequent data points: EMA = Closing price x multiplier + EMA (previous day) x (1-multiplier)</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading">Bollinger Bands</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Bollinger Bands are a popular technical analysis tool used to identify market volatility and potential price movements in financial markets. They are named after their creator, John Bollinger.</p>



<p>Bollinger Bands consist of three lines that are plotted on a price chart. The middle line is a simple moving average (SMA) of the asset price over a specified period (typically 20 days). The upper and lower lines are calculated by adding and subtracting a multiple (usually two) of the standard deviation of the asset price from the middle line.</p>



<p>The upper band is calculated as: Middle band + (2 x Standard deviation) The lower band is calculated as: Middle band &#8211; (2 x Standard deviation)</p>



<p>The standard deviation is a measure of how much the asset price deviates from the average. When the asset price is more volatile, the bands widen, and when the price is less volatile, the bands narrow.</p>



<p>Traders use Bollinger Bands to identify potential buy or sell signals. When the price touches or crosses the upper band, it may be a sell signal, indicating that the asset is overbought. Conversely, when the price touches or crosses the lower band, it may be a buy signal, indicating that the asset is oversold.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h2 class="wp-block-heading" id="h-feature-engineering-for-time-series-prediction-models-in-python">Feature Engineering for Time Series Prediction Models in Python</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In the following, this tutorial will guide you through the process of implementing a multivariate time series prediction model for the NASDAQ stock market index. Our aim is to equip you with the knowledge and practical skills required to create a powerful predictive model that can effectively forecast stock prices.</p>



<p>Throughout this tutorial, we will take you through a step-by-step approach to building a multivariate time series prediction model. You will learn how to implement and utilize different features to train and measure the performance of your model. Our goal is to ensure that you are not only able to understand the underlying concepts of multivariate time series prediction, but that you are also capable of applying these concepts in a practical setting.</p>



<p>The code is available on the GitHub repository.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_f47875-58"><a class="kb-button kt-button button kb-btn_a01882-be kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/01%20Time%20Series%20Forecasting%20%26%20Regression/008%20Feature%20Engineering%20for%20Multivariate%20Models.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_35290c-df kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly GitHub Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full"><img decoding="async" width="496" height="500" data-attachment-id="12671" data-permalink="https://www.relataly.com/robot-artificial-intelligence-colorful-midjourney-relataly-min/" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png" data-orig-size="496,500" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="robot artificial intelligence colorful midjourney relataly-min" data-image-description="&lt;p&gt;Let&amp;#8217;s do some feature engineering for machine learning!&lt;/p&gt;
" data-image-caption="&lt;p&gt;Let&amp;#8217;s do some feature engineering for machine learning!&lt;/p&gt;
" data-large-file="https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png" src="https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png" alt="Let's do some feature engineering for machine learning!" class="wp-image-12671" srcset="https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png 496w, https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png 298w, https://www.relataly.com/wp-content/uploads/2023/03/robot-artificial-intelligence-colorful-midjourney-relataly-min.png 140w" sizes="(max-width: 496px) 100vw, 496px" /><figcaption class="wp-element-caption">Let&#8217;s do some feature engineering for machine learning!</figcaption></figure>
</div>
</div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p>Before starting the coding part, make sure that you have set up your <a href="https://www.python.org/downloads/" target="_blank" rel="noreferrer noopener">Python 3</a> environment and required packages. If you don&#8217;t have an environment, follow&nbsp;<a href="https://www.relataly.com/anaconda-python-environment-machine-learning/1663/" target="_blank" rel="noreferrer noopener">this tutorial</a>&nbsp;to set up the&nbsp;<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda environment</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><a href="https://docs.python.org/3/library/math.html" target="_blank" rel="noreferrer noopener"><em>math</em></a></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>



<li><a href="https://seaborn.pydata.org/" target="_blank" rel="noreferrer noopener">Seaborn</a></li>
</ul>



<p>In addition, we will be using <em><a href="https://keras.io/" target="_blank" rel="noreferrer noopener">Keras</a></em>&nbsp;(2.0 or higher) with Tensorflow backend to train the neural network, the machine learning library scikit-learn, and the <a href="https://pandas-datareader.readthedocs.io/en/latest/" target="_blank" rel="noreferrer noopener">pandas-DataReader</a>. You can install these packages using the following console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>



<h3 class="wp-block-heading" id="h-step-1-load-the-data">Step #1 Load the Data</h3>



<p>Let&#8217;s start by setting up the imports and loading the data. Our Python project will use price data from the&nbsp;<a href="https://en.wikipedia.org/wiki/Nasdaq" target="_blank" rel="noreferrer noopener">NASDAQ</a>&nbsp;composite index&nbsp;<strong>(symbol: ^IXIC)</strong>&nbsp;from yahoo.finance.com.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Time Series Forecasting - Feature Engineering For Multivariate Models (Stock Market Prediction Example)
# A tutorial for this file is available at www.relataly.com

import math # Mathematical functions  
import numpy as np # Fundamental package for scientific computing with Python 
import pandas as pd # Additional functions for analysing and manipulating data 
from datetime import date # Date Functions 
import matplotlib.pyplot as plt # Important package for visualization - we use this to plot the market data 
import matplotlib.dates as mdates # Formatting dates 
from sklearn.metrics import mean_absolute_error, mean_squared_error # Packages for measuring model performance / errors 
import tensorflow as tf
from tensorflow.keras.models import Sequential # Deep learning library, used for neural networks 
from tensorflow.keras.layers import LSTM, Dense, Dropout # Deep learning classes for recurrent and regular densely-connected layers 
from tensorflow.keras.callbacks import EarlyStopping # EarlyStopping during model training 
from sklearn.preprocessing import RobustScaler # This Scaler removes the median and scales the data according to the quantile range to normalize the price data  
#from keras.optimizers import Adam # For detailed configuration of the optimizer 
import seaborn as sns # Visualization
sns.set_style('white', { 'axes.spines.right': False, 'axes.spines.top': False})


# check the tensorflow version and the number of available GPUs
print('Tensorflow Version: ' + tf.__version__)
physical_devices = tf.config.list_physical_devices('GPU')
print(&quot;Num GPUs:&quot;, len(physical_devices))

# Setting the timeframe for the data extraction
end_date =  date.today().strftime(&quot;%Y-%m-%d&quot;)
start_date = '2010-01-01'

# Getting NASDAQ quotes
stockname = 'NASDAQ'
symbol = '^IXIC'

# You can either use webreader or yfinance to load the data from yahoo finance
# import pandas_datareader as webreader
# df = webreader.DataReader(symbol, start=start_date, end=end_date, data_source=&quot;yahoo&quot;)

import yfinance as yf #Alternative package if webreader does not work: pip install yfinance
df = yf.download(symbol, start=start_date, end=end_date)

# Quick overview of dataset
df.head()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Tensorflow Version: 2.5.0
Num GPUs: 1
[*********************100%***********************]  1 of 1 completed
			Open		High		Low	Close	Adj 		Close		Volume
Date						
2009-12-31	2292.919922	2293.590088	2269.110107	2269.149902	2269.149902	1237820000
2010-01-04	2294.409912	2311.149902	2294.409912	2308.419922	2308.419922	1931380000
2010-01-05	2307.270020	2313.729980	2295.620117	2308.709961	2308.709961	2367860000
2010-01-06	2307.709961	2314.070068	2295.679932	2301.090088	2301.090088	2253340000
2010-01-07	2298.090088	2301.300049	2285.219971	2300.050049	2300.050049	2270050000</pre></div>



<h3 class="wp-block-heading" id="h-step-2-explore-the-data">Step #2 Explore the Data</h3>



<p>Let&#8217;s take a quick look at the data by creating line charts for the columns of our data set.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot line charts
df_plot = df.copy()

ncols = 2
nrows = int(round(df_plot.shape[1] / ncols, 0))

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, sharex=True, figsize=(14, 7))
for i, ax in enumerate(fig.axes):
        sns.lineplot(data = df_plot.iloc[:, i], ax=ax)
        ax.tick_params(axis=&quot;x&quot;, rotation=30, labelsize=10, length=0)
        ax.xaxis.set_major_locator(mdates.AutoDateLocator())
fig.tight_layout()
plt.show()</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1000" height="496" data-attachment-id="8645" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/line-plots-feature-engineering-1/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png" data-orig-size="1000,496" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="line-plots-feature-engineering-1" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png" src="https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png" alt="feature engineering stock market prediction, python tutorial, keras, scikit-learn" class="wp-image-8645" srcset="https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png 1000w, https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/line-plots-feature-engineering-1.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure>



<p>Our initial dataset includes six features: High, Low, Open, Close, Volumen, and Adj Close.</p>



<h3 class="wp-block-heading" id="h-step-3-feature-engineering">Step #3 Feature Engineering</h3>



<p>Now comes the exciting part &#8211;  we will implement additional features. We use various indicators from chart analysis, such as averages for different periods and stochastic oscillators to measure price momentum.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Indexing Batches
train_df = df.sort_values(by=['Date']).copy()

# Adding Month and Year in separate columns
d = pd.to_datetime(train_df.index)
train_df['Day'] = d.strftime(&quot;%d&quot;) 
train_df['Month'] = d.strftime(&quot;%m&quot;) 
train_df['Year'] = d.strftime(&quot;%Y&quot;) 
train_df</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">			Open		High		Low			Close		Adj Close	Volume		Day	Month	Year
Date									
2009-12-31	2292.919922	2293.590088	2269.110107	2269.149902	2269.149902	1237820000	31	12		2009
2010-01-04	2294.409912	2311.149902	2294.409912	2308.419922	2308.419922	1931380000	04	01		2010
2010-01-05	2307.270020	2313.729980	2295.620117	2308.709961	2308.709961	2367860000	05	01		2010
2010-01-06	2307.709961	2314.070068	2295.679932	2301.090088	2301.090088	2253340000	06	01		2010
2010-01-07	2298.090088	2301.300049	2285.219971	2300.050049	2300.050049	2270050000	07	01		2010</pre></div>



<p>We create a set of indicators for the training data with the following code. However, we will make one more restriction in the next step since a model with all these indicators does not achieve good results and would take far too long to train on a local computer.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Feature Engineering
def createFeatures(df):
    df = pd.DataFrame(df)

    
    df['Close_Diff'] = df['Adj Close'].diff()
        
    # Moving averages - different periods
    df['MA200'] = df['Close'].rolling(window=200).mean() 
    df['MA100'] = df['Close'].rolling(window=100).mean() 
    df['MA50'] = df['Close'].rolling(window=50).mean() 
    df['MA26'] = df['Close'].rolling(window=26).mean() 
    df['MA20'] = df['Close'].rolling(window=20).mean() 
    df['MA12'] = df['Close'].rolling(window=12).mean() 
    
    # SMA Differences - different periods
    df['DIFF-MA200-MA50'] = df['MA200'] - df['MA50']
    df['DIFF-MA200-MA100'] = df['MA200'] - df['MA100']
    df['DIFF-MA200-CLOSE'] = df['MA200'] - df['Close']
    df['DIFF-MA100-CLOSE'] = df['MA100'] - df['Close']
    df['DIFF-MA50-CLOSE'] = df['MA50'] - df['Close']
    
    # Moving Averages on high, lows, and std - different periods
    df['MA200_low'] = df['Low'].rolling(window=200).min()
    df['MA14_low'] = df['Low'].rolling(window=14).min()
    df['MA200_high'] = df['High'].rolling(window=200).max()
    df['MA14_high'] = df['High'].rolling(window=14).max()
    df['MA20dSTD'] = df['Close'].rolling(window=20).std() 
    
    # Exponential Moving Averages (EMAS) - different periods
    df['EMA12'] = df['Close'].ewm(span=12, adjust=False).mean()
    df['EMA20'] = df['Close'].ewm(span=20, adjust=False).mean()
    df['EMA26'] = df['Close'].ewm(span=26, adjust=False).mean()
    df['EMA100'] = df['Close'].ewm(span=100, adjust=False).mean()
    df['EMA200'] = df['Close'].ewm(span=200, adjust=False).mean()

    # Shifts (one day before and two days before)
    df['close_shift-1'] = df.shift(-1)['Close']
    df['close_shift-2'] = df.shift(-2)['Close']

    # Bollinger Bands
    df['Bollinger_Upper'] = df['MA20'] + (df['MA20dSTD'] * 2)
    df['Bollinger_Lower'] = df['MA20'] - (df['MA20dSTD'] * 2)
    
    # Relative Strength Index (RSI)
    df['K-ratio'] = 100*((df['Close'] - df['MA14_low']) / (df['MA14_high'] - df['MA14_low']) )
    df['RSI'] = df['K-ratio'].rolling(window=3).mean() 

    # Moving Average Convergence/Divergence (MACD)
    df['MACD'] = df['EMA12'] - df['EMA26']
    
    # Replace nas 
    nareplace = df.at[df.index.max(), 'Close']    
    df.fillna((nareplace), inplace=True)
    
    return df</pre></div>



<p>Now that we have created several features, we will limit them. We can now choose from these features and test how different feature combinations affect model performance.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># List of considered Features
FEATURES = [
#             'High',
#             'Low',
#             'Open',
              'Close',
#             'Volume',
#             'Day',
#             'Month',
#             'Year',
#             'Adj Close',
#              'close_shift-1',
#              'close_shift-2',
#             'MACD',
#             'RSI',
#             'MA200',
#             'MA200_high',
#             'MA200_low',
            'Bollinger_Upper',
            'Bollinger_Lower',
#             'MA100',            
#             'MA50',
#             'MA26',
#             'MA14_low',
#             'MA14_high',
#             'MA12',
#             'EMA20',
#             'EMA100',
#             'EMA200',
#               'DIFF-MA200-MA50',
#               'DIFF-MA200-MA100',
#             'DIFF-MA200-CLOSE',
#             'DIFF-MA100-CLOSE',
#             'DIFF-MA50-CLOSE'
           ]

# Create the dataset with features
df_features = createFeatures(train_df)

# Shift the timeframe by 10 month
use_start_date = pd.to_datetime(&quot;2010-11-01&quot; )
df_features = df_features[df_features.index &gt; use_start_date].copy()

# Filter the data to the list of FEATURES
data_filtered_ext = df_features[FEATURES].copy()

# We add a prediction column and set dummy values to prepare the data for scaling
#data_filtered_ext['Prediction'] = data_filtered_ext['Close'] 
print(data_filtered_ext.tail().to_string())

# remove Date column before training
dfs = data_filtered_ext.copy()

# Create a list with the relevant columns
assetname_list = [dfs.columns[i-1] for i in range(dfs.shape[1])]

# Create the lineplot
fig, ax = plt.subplots(figsize=(16, 8))
sns.lineplot(data=data_filtered_ext[assetname_list], linewidth=1.0, dashes=False, palette='muted')

# Configure and show the plot    
ax.set_title(stockname + ' price chart')
ax.legend()
plt.show</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}"> 			Close  			Bollinger_Upper  Bollinger_Lower
Date                                                      
2022-05-18  11418.150391     13404.779247     11065.040772
2022-05-19  11388.500000     13285.741255     11005.463725
2022-05-20  11354.620117     13214.664450     10928.073538
2022-05-23  11535.269531     13075.594634     10920.185347
2022-05-24  11264.450195     13035.543222     10837.607755
&lt;function matplotlib.pyplot.show(close=None, block=None)&gt;</pre></div>



<figure class="wp-block-image size-full is-resized"><img decoding="async" data-attachment-id="11456" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/image-7/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/12/image.png" data-orig-size="942,492" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/12/image.png" src="https://www.relataly.com/wp-content/uploads/2022/12/image.png" alt="Price chart for the nasdaq price index with bollinger bands" class="wp-image-11456" width="1124" height="586" srcset="https://www.relataly.com/wp-content/uploads/2022/12/image.png 942w, https://www.relataly.com/wp-content/uploads/2022/12/image.png 300w, https://www.relataly.com/wp-content/uploads/2022/12/image.png 768w" sizes="(max-width: 1124px) 100vw, 1124px" /></figure>



<h3 class="wp-block-heading" id="h-step-4-scaling-and-transforming-the-data">Step #4 Scaling and Transforming the Data</h3>



<p>Before training our model, we need to transform the data. This step includes scaling the data (to a range between 0 and 1) and dividing it into separate sets for training and testing the prediction model. Most of the code used in this section stems from the previous article on <a href="https://www.relataly.com/stock-market-prediction-with-multivariate-time-series-in-python/1815/" target="_blank" rel="noreferrer noopener">multivariate time-series prediction</a>, which covers the steps to transform the data. So we don&#8217;t go into too much detail here. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Calculate the number of rows in the data
nrows = dfs.shape[0]
np_data_unscaled = np.reshape(np.array(dfs), (nrows, -1))
print(np_data_unscaled.shape)

# Transform the data by scaling each feature to a range between 0 and 1
scaler = RobustScaler()
np_data = scaler.fit_transform(np_data_unscaled)

# Creating a separate scaler that works on a single column for scaling predictions
scaler_pred = RobustScaler()
df_Close = pd.DataFrame(data_filtered_ext['Close'])
np_Close_scaled = scaler_pred.fit_transform(df_Close)</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Out: (2619, 6)</pre></div>



<p>Once we have scaled the data, we will split the data into a train and test set. This step creates four datasets x_train and x_test, and y_train and y_test. x_train and x_test contain the data with our selected features. The two sets, y_train and y_test, have the actual values, which our model will try to predict.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Set the sequence length - this is the timeframe used to make a single prediction
sequence_length = 50 # = number of neurons in the first layer of the neural network

# Split the training data into train and train data sets
# As a first step, we get the number of rows to train the model on 80% of the data 
train_data_len = math.ceil(np_data.shape[0] * 0.8)

# Create the training and test data
train_data = np_data[:train_data_len, :]
test_data = np_data[train_data_len - sequence_length:, :]

# The RNN needs data with the format of [samples, time steps, features]
# Here, we create N samples, sequence_length time steps per sample, and 6 features
def partition_dataset(sequence_length, data):
    x, y = [], []
    data_len = data.shape[0]

    for i in range(sequence_length, data_len):
        x.append(data[i-sequence_length:i,:]) #contains sequence_length values 0-sequence_length * columsn
        y.append(data[i, 0]) #contains the prediction values for validation,  for single-step prediction
    
    # Convert the x and y to numpy arrays
    x = np.array(x)
    y = np.array(y)
    return x, y

# Generate training data and test data
x_train, y_train = partition_dataset(sequence_length, train_data)
x_test, y_test = partition_dataset(sequence_length, test_data)

# Print the shapes: the result is: (rows, training_sequence, features) (prediction value, )
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

# Validate that the prediction value and the input match up
# The last close price of the second input sample should equal the first prediction value
print(x_train[1][sequence_length-1][0])
print(y_train[0])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;disableCopy&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Out:
(1914, 30, 3) (1914,) 
(486, 30, 3) (486,)</pre></div>



<h3 class="wp-block-heading" id="h-step-5-train-the-time-series-forecasting-model">Step #5 Train the Time Series Forecasting Model</h3>



<p>Now that we have prepared the data, we can train our forecasting model. For this purpose, we will use a recurrent neural network from the Keras library. A recurrent neural network (RNN) is a type of artificial neural network that can process sequential data, such as text, audio, or time series data. Unlike traditional feedforward neural networks, in which data flows through the network in only one direction, RNNs have connections that form a directed cycle, allowing information to flow in multiple directions and be processed in a temporal manner.</p>



<p>The model architecture of our RNN looks as follows:</p>



<ul class="wp-block-list">
<li>LSTM layer that receives a mini-batch as input.</li>



<li>LSTM layer that has the same number of neurons as the mini-batch</li>



<li>Another LSTM layer that does not return the sequence</li>



<li>Dense layer with 32 neurons</li>



<li>Dense layer with one neuron that outputs the forecast</li>
</ul>



<p>The architecture is not too complex and is suitable for experimenting with different features. I arrived at this architecture by trying out different layers and configurations. However, I did not spend too much time fine-tuning the architecture since this tutorial focuses on feature engineering.</p>



<p>During model training, the neural network processes several mini-batches. The shape of the mini-batch is defined by the number of features and the period chosen. Multiplying these two dimensions (number of features x number of time steps) gives the input shape of our model.</p>



<p>The following code defines the model architecture, trains the model, and then prints the training loss curve:</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Configure the neural network model
model = Sequential()

# Configure the Neural Network Model with n Neurons - inputshape = t Timestamps x f Features
n_neurons = x_train.shape[1] * x_train.shape[2]
print('timesteps: ' + str(x_train.shape[1]) + ',' + ' features:' + str(x_train.shape[2]))
model.add(LSTM(n_neurons, return_sequences=True, input_shape=(x_train.shape[1], x_train.shape[2]))) 
#model.add(Dropout(0.1))
model.add(LSTM(n_neurons, return_sequences=True))
#model.add(Dropout(0.1))
model.add(LSTM(n_neurons, return_sequences=False))
model.add(Dense(32))
model.add(Dense(1, activation='relu'))


# Configure the Model   
optimizer='adam'; loss='mean_squared_error'; epochs = 100; batch_size = 32; patience = 8; 

# uncomment to customize the learning rate
learn_rate = &quot;standard&quot; # 0.05
# adam = Adam(learn_rate=learn_rate) 

parameter_list = ['epochs ' + str(epochs), 'batch_size ' + str(batch_size), 'patience ' + str(patience), 'optimizer ' + str(optimizer) + ' with learn rate ' + str(learn_rate), 'loss ' + str(loss)]
print('Parameters: ' + str(parameter_list))

# Compile and Training the model
model.compile(optimizer=optimizer, loss=loss)
early_stop = EarlyStopping(monitor='loss', patience=patience, verbose=1)
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, callbacks=[early_stop], shuffle = True,
                  validation_data=(x_test, y_test))

# Plot training &amp; validation loss values
fig, ax = plt.subplots(figsize=(12, 6), sharex=True)
plt.plot(history.history[&quot;loss&quot;])
plt.title(&quot;Model loss&quot;)
plt.ylabel(&quot;Loss&quot;)
plt.xlabel(&quot;Epoch&quot;)
ax.xaxis.set_major_locator(plt.MaxNLocator(epochs))
plt.legend([&quot;Train&quot;, &quot;Test&quot;], loc=&quot;upper left&quot;)
plt.show()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">timesteps: 50, features:1
Parameters: ['epochs 100', 'batch_size 32', 'patience 8', 'optimizer adam with learn rate standard', 'loss mean_squared_error']
Epoch 1/100
72/72 [==============================] - 9s 55ms/step - loss: 0.0990 - val_loss: 0.2985
Epoch 2/100
72/72 [==============================] - 3s 37ms/step - loss: 0.0932 - val_loss: 0.1768
Epoch 3/100
72/72 [==============================] - 3s 39ms/step - loss: 0.0931 - val_loss: 0.1246
Epoch 4/100
72/72 [==============================] - 3s 37ms/step - loss: 0.0931 - val_loss: 0.0902
Epoch 5/100
72/72 [==============================] - 3s 38ms/step - loss: 0.0929 - val_loss: 0.0846
Epoch 6/100
72/72 [==============================] - 3s 38ms/step - loss: 0.0930 - val_loss: 0.0611
Epoch 7/100
72/72 [==============================] - 3s 38ms/step - loss: 0.0929 - val_loss: 0.0498
Epoch 8/100
72/72 [==============================] - 3s 37ms/step - loss: 0.0928 - val_loss: 0.0208
Epoch 9/100
72/72 [==============================] - 3s 38ms/step - loss: 0.0929 - val_loss: 0.0588
Epoch 10/100
72/72 [==============================] - 3s 37ms/step - loss: 0.0928 - val_loss: 0.0437
Epoch 11/100
72/72 [==============================] - 3s 36ms/step - loss: 0.0928 - val_loss: 0.0192
Epoch 12/100
...
72/72 [==============================] - 3s 38ms/step - loss: 0.0925 - val_loss: 0.0094
Epoch 46/100
72/72 [==============================] - 3s 37ms/step - loss: 0.0925 - val_loss: 0.0113
Epoch 00046: early stopping</pre></div>



<figure class="wp-block-image size-full is-resized"><img decoding="async" data-attachment-id="8656" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/loss-function-feature-engineering-neural-networks/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/loss-function-feature-engineering-neural-networks.png" data-orig-size="729,383" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="loss-function-feature-engineering-neural-networks" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/loss-function-feature-engineering-neural-networks.png" src="https://www.relataly.com/wp-content/uploads/2022/05/loss-function-feature-engineering-neural-networks.png" alt="loss curve of our time series prediction model for stock market forecasting" class="wp-image-8656" width="775" height="407" srcset="https://www.relataly.com/wp-content/uploads/2022/05/loss-function-feature-engineering-neural-networks.png 729w, https://www.relataly.com/wp-content/uploads/2022/05/loss-function-feature-engineering-neural-networks.png 300w" sizes="(max-width: 775px) 100vw, 775px" /></figure>



<p>The loss drops quickly, and the training process looks promising.</p>



<h3 class="wp-block-heading" id="h-step-6-evaluate-model-performance">Step #6 Evaluate Model Performance</h3>



<p>If we test a feature, we also want to know how it impacts the performance of our model. Feature Engineering is therefore closely related to evaluating model performance. So, let&#8217;s check the prediction performance. For this purpose, we score the model with the test data set (x_test). Then we can compare the predictions with the actual values (y_test) in a lineplot.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the predicted values
y_pred_scaled = model.predict(x_test)

# Unscale the predicted values
y_pred = scaler_pred.inverse_transform(y_pred_scaled)
y_test_unscaled = scaler_pred.inverse_transform(y_test.reshape(-1, 1))
y_test_unscaled.shape

# Mean Absolute Error (MAE)
MAE = mean_absolute_error(y_test_unscaled, y_pred)
print(f'Median Absolute Error (MAE): {np.round(MAE, 2)}')

# Mean Absolute Percentage Error (MAPE)
MAPE = np.mean((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled))) * 100
print(f'Mean Absolute Percentage Error (MAPE): {np.round(MAPE, 2)} %')

# Median Absolute Percentage Error (MDAPE)
MDAPE = np.median((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled)) ) * 100
print(f'Median Absolute Percentage Error (MDAPE): {np.round(MDAPE, 2)} %')

# The date from which on the date is displayed
display_start_date = &quot;2019-01-01&quot; 

# Add the difference between the valid and predicted prices
train = pd.DataFrame(dfs['Close'][:train_data_len + 1]).rename(columns={'Close': 'y_train'})
valid = pd.DataFrame(dfs['Close'][train_data_len:]).rename(columns={'Close': 'y_test'})
valid.insert(1, &quot;y_pred&quot;, y_pred, True)
valid.insert(1, &quot;residuals&quot;, valid[&quot;y_pred&quot;] - valid[&quot;y_test&quot;], True)
df_union = pd.concat([train, valid])

# Zoom in to a closer timeframe
df_union_zoom = df_union[df_union.index &gt; display_start_date]

# Create the lineplot
fig, ax1 = plt.subplots(figsize=(16, 8))
plt.title(&quot;y_pred vs y_test&quot;)
plt.ylabel(stockname, fontsize=18)
sns.set_palette([&quot;#090364&quot;, &quot;#1960EF&quot;, &quot;#EF5919&quot;])
sns.lineplot(data=df_union_zoom[['y_pred', 'y_train', 'y_test']], linewidth=1.0, dashes=False, ax=ax1)

# Create the barplot for the absolute errors
df_sub = [&quot;#2BC97A&quot; if x &gt; 0 else &quot;#C92B2B&quot; for x in df_union_zoom[&quot;residuals&quot;].dropna()]
ax1.bar(height=df_union_zoom['residuals'].dropna(), x=df_union_zoom['residuals'].dropna().index, width=3, label='absolute errors', color=df_sub)
plt.legend()
plt.show()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Median Absolute Error (MAE): 547.23 
Mean Absolute Percentage Error (MAPE): 4.04 % 
Median Absolute Percentage Error (MDAPE): 3.73 %</pre></div>



<figure class="wp-block-image size-full is-resized"><img decoding="async" data-attachment-id="8654" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/lineplot-nasdaq-feature-engineering-1/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png" data-orig-size="942,492" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="lineplot-nasdaq-feature-engineering-1" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png" src="https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png" alt="multivariate feature engineering, prediction results" class="wp-image-8654" width="1164" height="607" srcset="https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png 942w, https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/lineplot-nasdaq-feature-engineering-1.png 768w" sizes="(max-width: 1164px) 100vw, 1164px" /></figure>



<p>On average, the predictions of our model deviate from the actual values by about one percent. Although one percent may not sound like a lot, the prediction errors can quickly accumulate to larger values.</p>



<h3 class="wp-block-heading" id="h-step-7-overview-of-selected-models">Step #7 Overview of Selected Models</h3>



<p>In writing this article, I tested various models based on different features. The neural network architecture remained unchanged. Likewise, I kept the hyperparameters the same except for the learning rate. Below are the results of these model variants:</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="3605" data-permalink="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/image-33-4/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2021/04/image-33.png" data-orig-size="753,944" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-33" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2021/04/image-33.png" src="https://www.relataly.com/wp-content/uploads/2021/04/image-33.png" alt="performance of different variations of the multivariate keras neural network model for stock market forecasting" class="wp-image-3605" width="587" height="736" srcset="https://www.relataly.com/wp-content/uploads/2021/04/image-33.png 753w, https://www.relataly.com/wp-content/uploads/2021/04/image-33.png 239w" sizes="(max-width: 587px) 100vw, 587px" /></figure>



<p></p>



<h3 class="wp-block-heading" id="h-step-8-conclusions">Step #8 Conclusions</h3>



<p>Estimating which indicators will lead to good results in advance is difficult. More indicators do not necessarily lead to better results because they increase the model complexity and add data without predictive power. This so-called noise makes it harder for the model to separate important influencing factors from less important ones. Also, each additional indicator increases the time needed to train the model. So there is no way around testing different variants.</p>



<p>Besides the feature, various hyperparameters such as the learning rate, optimizer, batch size, and the selected time frame of the data (sequence_length) impact the model&#8217;s performance. Tuning these hyperparameters can further improve model performance. </p>



<ul class="wp-block-list">
<li>A learning rate of 0.05 achieves the best results from the tested configurations.</li>



<li>Of all features, only the Bollinger bands positively affected the model&#8217;s performance. </li>



<li>As expected, the performance tends to decrease with the number of features. </li>



<li>In our case, the hyperparameters seem to affect the performance of the models more than the choice of features.</li>
</ul>



<p>Finally, we have optimized only a single parameter. We searched for optimal learning rates while leaving all other parameters unchanged, such as the optimizer, the neural network architecture, or the sequence length. Based on the results, we can draw several conclusions: </p>



<p>There is plenty of room for improvement and experimentation. With more time for experiments and computational power, it will undoubtedly be possible to identify better features and model configurations. So, have fun experimenting! 🙂</p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In this tutorial, we have delved into the fascinating world of feature engineering for stock market forecasting using Python. By exploring various features from chart analysis, such as RSI, moving averages, and Bollinger bands, we have developed multiple variants of a recurrent neural network that produce distinct prediction models.</p>



<p>Our experiments have shown that the choice of features can have a significant impact on the performance of the prediction model. Therefore, it&#8217;s essential to carefully select features and consider their potential impact on the model. Additionally, keep in mind that the most effective features for recognizing patterns in historical data will vary depending on the specific time series data being analyzed.</p>



<p>By following the crucial steps outlined in this tutorial, you now have the knowledge and tools to apply feature engineering techniques to any multivariate time series forecasting problem. With further experimentation and testing, you can fine-tune your models to achieve the best possible results for your specific use case.</p>



<p>We hope you found this tutorial both informative and helpful. If you have any questions or comments, don&#8217;t hesitate to reach out and let us know. </p>



<p>And if you want to learn more about feature preparation and exploration, check out my recent article on <a href="https://www.relataly.com/exploratory-feature-preparation-for-regression-with-python-and-scikit-learn/8832/" target="_blank" rel="noreferrer noopener">Exploratory Feature Preparation for Regression Models</a>.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h2 class="wp-block-heading" id="h-sources-and-further-reading">Sources and Further Reading</h2>



<ol class="wp-block-list"><li><a href="https://amzn.to/3MyU6Tj" target="_blank" rel="noreferrer noopener">Charu C. Aggarwal (2018) Neural Networks and Deep Learning</a></li><li><a href="https://amzn.to/3yIQdWi" target="_blank" rel="noreferrer noopener">Jansen (2020) Machine Learning for Algorithmic Trading: Predictive models to extract signals from market and alternative data for systematic trading strategies with Python</a></li><li><a href="https://amzn.to/3S9Nfkl" target="_blank" rel="noreferrer noopener">Aurélien Géron (2019) Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems </a></li><li><a href="https://amzn.to/3EKidwE" target="_blank" rel="noreferrer noopener">David Forsyth (2019) Applied Machine Learning Springer</a></li><li><a href="https://amzn.to/3MAy8j5" target="_blank" rel="noreferrer noopener">Andriy Burkov (2020) Machine Learning Engineering</a></li></ol>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>



<p><strong>Books on Applied Machine Learning</strong></p>



<div style="display: inline-block;">

  <iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=3030181162&amp;asins=3030181162&amp;linkId=669e46025028259138fbb5ccec12dfbe&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1999579577&amp;asins=1999579577&amp;linkId=91d862698bf9010ff4c09539e4c49bf4&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
</div>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>
<p>The post <a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/">Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1813</post-id>	</item>
		<item>
		<title>Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python</title>
		<link>https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/</link>
					<comments>https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/#comments</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Mon, 01 Jun 2020 17:20:46 +0000</pubDate>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Finance]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Stock Market Forecasting]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Time Series Forecasting]]></category>
		<category><![CDATA[Use Cases]]></category>
		<category><![CDATA[Advanced Tutorials]]></category>
		<category><![CDATA[AI in Finance]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Multivariate Models]]></category>
		<category><![CDATA[Stock Market Prediction]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=1815</guid>

					<description><![CDATA[<p>Regression models based on recurrent neural networks (RNN) can recognize patterns in time series data, making them an exciting technology for stock market forecasting. What distinguishes these RNNs from traditional neural networks is their architecture. It consists of multiple layers of long-term, short-term memory (LSTM). These LSTM layers allow the model to learn patterns in ... <a title="Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python" class="read-more" href="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/" aria-label="Read more about Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python">Read more</a></p>
<p>The post <a href="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/">Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Regression models based on recurrent neural networks (RNN) can recognize patterns in time series data, making them an exciting technology for stock market forecasting. What distinguishes these RNNs from traditional neural networks is their architecture. It consists of multiple layers of long-term, short-term memory (LSTM). These LSTM layers allow the model to learn patterns in a time series that occur over different periods and are often difficult for human analysts to detect. We can train such models with one feature (<a href="https://www.relataly.com/univariate-stock-market-forecasting-using-a-recurrent-neural-network/122/" target="_blank" rel="noreferrer noopener">univariate forecasting models</a>) or multiple features (multivariate models). Multivariate Models can take more data into account, and if we provide them with relevant features, they can make better predictions. This tutorial uses Python and Keras to implement a multivariate RNN for stock price prediction. We define the architecture of our regression model and then train this model to predict the NASDAQ index.</p>



<p>The remainder of this tutorial proceeds in two parts: We start with a brief intro in which we compare modeling univariate and multivariate time series data. Then we turn to the hands-on part, in which we prepare the multivariate time series data and use it to train a neural network in Python. The model is a recurrent neural network with LSTM layers that forecasts the NASDAQ stock market index. Finally, we evaluate the performance of our model and make a forecast for the next day.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<div class="wp-block-kadence-infobox kt-info-box_317393-a1"><span class="kt-blocks-info-box-link-wrap info-box-link kt-blocks-info-box-media-align-top kt-info-halign-left"><div class="kt-infobox-textcontent"><h2 class="kt-blocks-info-box-title">Disclaimer</h2><p class="kt-blocks-info-box-text">This article does not constitute financial advice. Stock markets can be very volatile and are generally difficult to predict. Predictive models and other forms of analytics applied in this article only serve the purpose of illustrating machine learning use cases.</p></div></span></div>
</div></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"><div class="wp-block-image">
<figure class="alignright size-large"><img decoding="async" width="1024" height="1024" data-attachment-id="12505" data-permalink="https://www.relataly.com/business-use-cases-for-openai-gpt-models-chatgpt-davinci/12200/blockchain-bull-cryptocurrencies-min/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png" data-orig-size="1024,1024" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="blockchain bull cryptocurrencies-min" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png" src="https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min-1024x1024.png" alt="" class="wp-image-12505" srcset="https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png 1024w, https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png 300w, https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png 140w, https://www.relataly.com/wp-content/uploads/2023/02/blockchain-bull-cryptocurrencies-min.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Stock market forecasting has become an exciting application for recurrent neural networks.</figcaption></figure>
</div></div>
</div>



<h2 class="wp-block-heading" id="h-univariate-vs-multivariate-time-series-models">Univariate vs. Multivariate Time Series Models</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Multivariate models and univariate models differ in the number of their input features. While univariate models consider only a single feature, multivariate models use several input variables (features). In stock market forecasting, we can create additional features from price history. Examples are performance indicators such as moving averages, the RSI, or the Sales Volume. We can also include features from other sources, for example, social media sentiment, weather forecasts, etc. Multivariate models that have additional relevant information available have a chance to outperform univariate models. However, this is only true if the features are relevant and are indicative of future price movements.</p>



<p>Preparing data for training univariate models is more straightforward than for multivariate models. If you are new to time series prediction, you might want to look at my earlier articles. These explain how to develop and evaluate univariate time series models:</p>



<ul class="wp-block-list">
<li><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/003%20Time%20Series%20Forecasting%20-%20Univariate%20Model%20using%20Recurrent%20Neural%20Networks.ipynb" target="_blank" rel="noreferrer noopener">Stock Market Forecasting using Univariate Models and Python</a></li>



<li><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/005%20Time%20Series%20Forecasting%20-%20Multi-step%20Rolling%20Forecasting.ipynb" target="_blank" rel="noreferrer noopener">Multi-step Time Series Forecasting with Python: Step-by-Step Guide</a></li>



<li><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/004%20Time%20Series%20Forecasting%20-%20Adjusting%20Prediction%20Intervals.ipynb" target="_blank" rel="noreferrer noopener">Stock Market Prediction – Adjusting Time Series Prediction Intervals</a></li>



<li><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/009%20Time%20Series%20Forecasting%20-%20Measuring%20Model%20Performance.ipynb" target="_blank" rel="noreferrer noopener">Evaluating Time Series Forecasting Models with Python</a></li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h4 class="wp-block-heading" id="h-univariate-prediction-models">Univariate Prediction Models</h4>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In time series regression, the standard approach is to train a model using past values from the time series that need to be predicted. The assumption is that the value of a time series at time t is closely related to the previous time steps t-1, t-2, t-3, and so on. This approach is similar to chart analysis, which involves identifying patterns in a price chart that can indicate future movements. Both approaches rely on the ability to identify recurring patterns in the data and make accurate predictions based on them. The performance of the model or analysis depends on the ability to identify these patterns and draw the right conclusions from them.</p>



<p>Several techniques can be used to improve the performance of time series regression models, including feature engineering, hyperparameter optimization, and ensemble methods. In addition to these techniques, it is also important to carefully evaluate the performance of the model using appropriate metrics, such as mean squared error or mean absolute error, and to continuously monitor the model&#8217;s performance to ensure it remains accurate over time.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-full is-resized"><img decoding="async" data-attachment-id="7379" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-19-8/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/image-19.png" data-orig-size="768,530" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-19" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/image-19.png" src="https://www.relataly.com/wp-content/uploads/2022/04/image-19.png" alt="univariate time series modelling, recurrent neural networks, keras, python, tutorials, stock market prediction" class="wp-image-7379" width="347" height="240" srcset="https://www.relataly.com/wp-content/uploads/2022/04/image-19.png 768w, https://www.relataly.com/wp-content/uploads/2022/04/image-19.png 300w" sizes="(max-width: 347px) 100vw, 347px" /><figcaption class="wp-element-caption">Univariate Time Series Prediction</figcaption></figure>
</div>
</div>



<h4 class="wp-block-heading" id="h-multivariate-prediction-models">Multivariate Prediction Models</h4>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Predicting the price of a financial asset is a challenging task due to the numerous variables that can influence it, including economic cycles, political events, unforeseen occurrences, psychological factors, market sentiment, and even the weather. These variables are often interdependent, which makes statistical modeling even more complex. While multivariate models can take into account several factors, they are still a simplification of reality and may not fully capture the complexity of the market. On the other hand, univariate models only consider a single dependent variable, ignoring the other dimensions.</p>



<p>Even with good features, predicting financial prices can be difficult because patterns and market rules may change frequently. As a result, models may make mistakes. However, as <a href="https://en.wikipedia.org/wiki/All_models_are_wrong" target="_blank" rel="noreferrer noopener">Georg Box</a> famously said, &#8220;All models are wrong, but some are useful.&#8221; Despite their limitations, multivariate models can provide a more detailed representation of reality compared to univariate models, and can still be useful in forecasting financial prices.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="7378" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-15/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/image-15.png" data-orig-size="1076,524" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-15" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/image-15.png" src="https://www.relataly.com/wp-content/uploads/2022/04/image-15-1024x499.png" alt="multivariate time series modelling, recurrent neural networks, keras, python, tutorials, stock market prediction" class="wp-image-7378" width="356" height="174" srcset="https://www.relataly.com/wp-content/uploads/2022/04/image-15.png 1024w, https://www.relataly.com/wp-content/uploads/2022/04/image-15.png 300w, https://www.relataly.com/wp-content/uploads/2022/04/image-15.png 768w, https://www.relataly.com/wp-content/uploads/2022/04/image-15.png 1076w" sizes="(max-width: 356px) 100vw, 356px" /><figcaption class="wp-element-caption">Multivariate Time Series Prediction</figcaption></figure>
</div>
</div>



<div style="height:8px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="h-implementing-a-multivariate-time-series-prediction-model-in-python">Implementing a Multivariate Time Series Prediction Model in Python</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Now that we have a solid understanding of multivariate time series forecasting, it&#8217;s time to put our knowledge into practice by building a model using Python and TensorFlow. Specifically, we will create a multivariate recurrent neural network (RNN) to predict the NASDAQ stock market index. RNNs are well-suited for time series forecasting because they can process sequential data, considering the dependencies between past and future events. </p>



<p>To build our RNN model, we will need to go through several essential steps. </p>



<ol class="wp-block-list">
<li>Creating features and scaling: This involves loading and preparing the time series data for modeling, including selecting the time period and relevant features and scaling the data.</li>



<li>Splitting the data: We split the data into train and test sets.</li>



<li>Sliding window approach: The time series data is sliced into mini-batches using the sliding window approach.</li>



<li>Model design and training: The appropriate architecture for the RNN model is chosen, and an optimization algorithm is used to adjust the model&#8217;s weights and biases to minimize prediction error.</li>



<li>Model validation and predictions: We evaluate the model&#8217;s performance by comparing the predicted values to the actual values of the NASDAQ index. In addition, we will use the model to make predictions about future events. </li>



<li>Unscaling the predictions: The predictions are unscaled to bring them back to their original scale.</li>
</ol>



<p>The code is available on the GitHub repository.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_332b7d-0a"><a class="kb-button kt-button button kb-btn_46b0bc-a4 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/01%20Time%20Series%20Forecasting%20%26%20Regression/007%20Multivariate%20Time%20Series%20Forecasting%20with%20RNNs.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_559184-98 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly Github Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="7446" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-20/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/04/image-20.png" data-orig-size="1807,1911" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-20" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/04/image-20.png" src="https://www.relataly.com/wp-content/uploads/2022/04/image-20-968x1024.png" alt="Six essential steps of training a multivariate recurrent neural network for time series prediction, stock market forecasting, Python, Keras, splitting, slicing, RNN architecture, multivariate time series modelling" class="wp-image-7446" width="735" height="777" srcset="https://www.relataly.com/wp-content/uploads/2022/04/image-20.png 968w, https://www.relataly.com/wp-content/uploads/2022/04/image-20.png 284w, https://www.relataly.com/wp-content/uploads/2022/04/image-20.png 768w, https://www.relataly.com/wp-content/uploads/2022/04/image-20.png 1452w, https://www.relataly.com/wp-content/uploads/2022/04/image-20.png 1807w" sizes="(max-width: 735px) 100vw, 735px" /><figcaption class="wp-element-caption">Six Essential Steps for Developing a Multivariate Time Series Model</figcaption></figure>
</div>
</div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p>Before starting the coding part, make sure that you have set up your <a href="https://www.python.org/downloads/" target="_blank" rel="noreferrer noopener">Python 3</a> environment and required packages. If you don&#8217;t have a Python environment, follow the steps in&nbsp;<a href="https://www.relataly.com/anaconda-python-environment-machine-learning/1663/" target="_blank" rel="noreferrer noopener">this tutorial</a>&nbsp;to set up the&nbsp;<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda environment</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><a href="https://docs.python.org/3/library/math.html" target="_blank" rel="noreferrer noopener">math</a></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>
</ul>



<p>In addition, we will be using <em><a href="https://keras.io/" target="_blank" rel="noreferrer noopener">Keras&nbsp;</a></em>(2.0 or higher) with <a href="https://www.tensorflow.org/" target="_blank" rel="noreferrer noopener"><em>Tensorflow</em> </a>backend, the machine learning library <a href="https://scikit-learn.org/stable/" target="_blank" rel="noreferrer noopener">sci-kit-learn</a>, and the <a href="https://pandas-datareader.readthedocs.io/en/latest/" target="_blank" rel="noreferrer noopener">pandas-DataReader</a>. </p>



<p>You can install packages using console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>



<h3 class="wp-block-heading" id="h-step-1-load-the-time-series-data">Step #1 Load the Time Series Data</h3>



<p>Let&#8217;s start by loading price data on the <a href="https://en.wikipedia.org/wiki/Nasdaq" target="_blank" rel="noreferrer noopener">NASDAQ</a> composite index <strong>(symbol: ^IXIC)</strong> from <a href="https://finance.yahoo.com/" target="_blank" rel="noreferrer noopener">yahoo.finance.com</a> into our Python project. To download the data, we use <a href="https://pandas-datareader.readthedocs.io/en/latest/" target="_blank" rel="noreferrer noopener">Pandas DataReader</a> &#8211; a popular Python library that provides functions to extract data from various sources on the web. Alternatively, you can also use the &#8220;yfinance&#8221; library.</p>



<p>We provide the technical symbol for the NASDAQ index, &#8220;^IXIC.&#8221; Alternatively, you could use other asset symbols, for example, BTC-USD, to get price quotes for Bitcoin. In addition, we limit the data in the API request to the timeframe between 2010-01-01 and the current date.</p>



<p>Running the code below will load the data into a new DataFrame object. Be aware that input data and predictions will vary depending on when you execute the code.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Time Series Forecasting - Multivariate Time Series Models for Stock Market Prediction

import math # Mathematical functions 
import numpy as np # Fundamental package for scientific computing with Python
import pandas as pd # Additional functions for analysing and manipulating data
from datetime import date, timedelta, datetime # Date Functions
from pandas.plotting import register_matplotlib_converters # This function adds plotting functions for calender dates
import matplotlib.pyplot as plt # Important package for visualization - we use this to plot the market data
import matplotlib.dates as mdates # Formatting dates
import tensorflow as tf
from sklearn.metrics import mean_absolute_error, mean_squared_error # Packages for measuring model performance / errors
from tensorflow.keras import Sequential # Deep learning library, used for neural networks
from tensorflow.keras.layers import LSTM, Dense, Dropout # Deep learning classes for recurrent and regular densely-connected layers
from tensorflow.keras.callbacks import EarlyStopping # EarlyStopping during model training
from sklearn.preprocessing import RobustScaler, MinMaxScaler # This Scaler removes the median and scales the data according to the quantile range to normalize the price data 
import seaborn as sns # Visualization
sns.set_style('white', { 'axes.spines.right': False, 'axes.spines.top': False})

# check the tensorflow version and the number of available GPUs
print('Tensorflow Version: ' + tf.__version__)
physical_devices = tf.config.list_physical_devices('GPU')
print(&quot;Num GPUs:&quot;, len(physical_devices))

# Setting the timeframe for the data extraction
end_date =  date.today().strftime(&quot;%Y-%m-%d&quot;)
start_date = '2010-01-01'

# Getting NASDAQ quotes
stockname = 'NASDAQ'
symbol = '^IXIC'

# You can either use webreader or yfinance to load the data from yahoo finance
# import pandas_datareader as webreader
# df = webreader.DataReader(symbol, start=start_date, end=end_date, data_source=&quot;yahoo&quot;)

import yfinance as yf #Alternative package if webreader does not work: pip install yfinance
df = yf.download(symbol, start=start_date, end=end_date)

# Create a quick overview of the dataset
df.head()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Tensorflow Version: 2.5.0
Num GPUs: 1
[*********************100%***********************]  1 of 1 completed
			Open		High		Low			Close		Adj Close	Volume
Date						
2009-12-31	2292.919922	2293.590088	2269.110107	2269.149902	2269.149902	1237820000
2010-01-04	2294.409912	2311.149902	2294.409912	2308.419922	2308.419922	1931380000
2010-01-05	2307.270020	2313.729980	2295.620117	2308.709961	2308.709961	2367860000
2010-01-06	2307.709961	2314.070068	2295.679932	2301.090088	2301.090088	2253340000
2010-01-07	2298.090088	2301.300049	2285.219971	2300.050049	2300.050049	2270050000</pre></div>



<p>The data looks as expected and has the following columns:</p>



<ul class="wp-block-list">
<li>High &#8211; the daily high</li>



<li>Low &#8211; the daily low</li>



<li>Open &#8211; the opening price</li>



<li>Close &#8211; the closing price </li>



<li>Volume &#8211; the daily trading volume</li>



<li>Adj Close &#8211; the adjacent closing price</li>
</ul>



<h3 class="wp-block-heading" id="h-step-2-explore-the-data">Step #2 Explore the Data</h3>



<p>Let&#8217;s first familiarize ourselves with the data before processing them further. Line plots are an excellent choice to gain a quick overview of time series data. By running the code below, we loop over the columns to plot a line chart for each column of the dataframe. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot line charts
df_plot = df.copy()

ncols = 2
nrows = int(round(df_plot.shape[1] / ncols, 0))

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, sharex=True, figsize=(14, 7))
for i, ax in enumerate(fig.axes):
        sns.lineplot(data = df_plot.iloc[:, i], ax=ax)
        ax.tick_params(axis=&quot;x&quot;, rotation=30, labelsize=10, length=0)
        ax.xaxis.set_major_locator(mdates.AutoDateLocator())
fig.tight_layout()
plt.show()</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="1000" height="496" data-attachment-id="11767" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-18/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/12/image-18.png" data-orig-size="1000,496" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-18" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/12/image-18.png" src="https://www.relataly.com/wp-content/uploads/2022/12/image-18.png" alt="chart of the NASDAQ index created with python" class="wp-image-11767" srcset="https://www.relataly.com/wp-content/uploads/2022/12/image-18.png 1000w, https://www.relataly.com/wp-content/uploads/2022/12/image-18.png 300w, https://www.relataly.com/wp-content/uploads/2022/12/image-18.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></figure>



<p>The line plots look as expected. We continue with preprocessing and feature engineering. </p>



<h3 class="wp-block-heading" id="h-step-3-feature-selection-and-scaling">Step #3 Feature Selection and Scaling</h3>



<p>Before we can train the neural network, we need to transform the data into a processable shape. In this section, we perform the following tasks:</p>



<ul class="wp-block-list">
<li>Selecting features</li>



<li>Scaling the data to a standard value range</li>
</ul>



<h4 class="wp-block-heading">3.1 Selecting Features</h4>



<p>First, we will select the features upon which we want to train our neural network. The selection and engineering of relevant feature variables is a complex topic. We could also create additional features such as moving averages, but I want to keep things simple. Therefore, we select features that are already present in our data. To learn more about feature engineering for stock market prediction, check out the<a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/" target="_blank" rel="noreferrer noopener"> relataly feature engineering tutorial</a>.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="1886" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/feature-selection/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png" data-orig-size="1596,759" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Feature-Selection" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png" src="https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection-1024x487.png" alt="Illustration how we preprocess the stock market data, before we use them to train a multivariate time series regression model" class="wp-image-1886" width="695" height="330" srcset="https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png 1024w, https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png 300w, https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png 768w, https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png 1536w, https://www.relataly.com/wp-content/uploads/2020/05/Feature-Selection.png 1596w" sizes="(max-width: 695px) 100vw, 695px" /><figcaption class="wp-element-caption">Feature Selection of Multivariate Time Series Models</figcaption></figure>



<p>Running the code below selects the features. We add a dummy column to our record called &#8220;Predictions,&#8221; which will help us later when we need to reverse the scaling of our data.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Indexing Batches
train_df = df.sort_values(by=['Date']).copy()

# List of considered Features
FEATURES = ['High', 'Low', 'Open', 'Close', 'Volume'
            #, 'Month', 'Year', 'Adj Close'
           ]

print('FEATURE LIST')
print([f for f in FEATURES])

# Create the dataset with features and filter the data to the list of FEATURES
data = pd.DataFrame(train_df)
data_filtered = data[FEATURES]

# We add a prediction column and set dummy values to prepare the data for scaling
data_filtered_ext = data_filtered.copy()
data_filtered_ext['Prediction'] = data_filtered_ext['Close']

# Print the tail of the dataframe
data_filtered_ext.tail()</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">FEATURE LIST
['High', 'Low', 'Open', 'Close', 'Volume']
			High			Low				Open			Close			Volume		Prediction
Date						
2022-05-09	11990.610352	11574.940430	11923.030273	11623.250000	5911380000	11623.250000
2022-05-10	11944.940430	11566.280273	11900.339844	11737.669922	6199090000	11737.669922
2022-05-11	11844.509766	11339.179688	11645.570312	11364.240234	6120860000	11364.240234
2022-05-12	11547.330078	11108.759766	11199.250000	11370.959961	6647400000	11370.959961
2022-05-13	11856.709961	11510.259766	11555.969727	11805.000000	5868610000	11805.000000</pre></div>



<h4 class="wp-block-heading" id="h-3-2-scaling-the-multivariate-input-data">3.2 Scaling the Multivariate Input Data</h4>



<p>Another necessary step in data preparation for neural networks is scaling the input data. Scaling will increase training times and improve model accuracy. The scikit-learn package offers different scaling approaches. We use the MinMaxScaler to scale the input data to a range between 0 and 1.</p>



<p>A model that is trained on scaled data will also produce scaled predictions. Therefore, when we make predictions later with our model, we must not forget to scale the predictions back. The scaler_model will adapt to the shape of the data (6-dimensional). However, our predictions will be one-dimensional. Because the scaler has a fixed input shape, we cannot simply reuse it for unscaling our model predictions. To unscale the predictions later, we create an additional scaler that works on a single feature column (scaler_pred).</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the number of rows in the data
nrows = data_filtered.shape[0]

# Convert the data to numpy values
np_data_unscaled = np.array(data_filtered)
np_data = np.reshape(np_data_unscaled, (nrows, -1))
print(np_data.shape)

# Transform the data by scaling each feature to a range between 0 and 1
scaler = MinMaxScaler()
np_data_scaled = scaler.fit_transform(np_data_unscaled)

# Creating a separate scaler that works on a single column for scaling predictions
scaler_pred = MinMaxScaler()
df_Close = pd.DataFrame(data_filtered_ext['Close'])
np_Close_scaled = scaler_pred.fit_transform(df_Close)</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Out: (2619, 6)</pre></div>



<h3 class="wp-block-heading" id="h-step-4-transforming-the-multivariate-data">Step #4 Transforming the Multivariate Data</h3>



<p>Next, we train our multivariate regression model based on a three-dimensional data structure. The first dimension is the sequences, the second dimension is the time steps (mini-batches), and the third dimension is the features. The illustration below shows the steps to bring the multivariate data into a shape our neural model can process during training. We must keep this form and perform the same steps when using the model to create a forecast. </p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p>An essential step in the preparation process is slicing the data into multiple input data sequences with associated target values. We write a simple Python script that uses a &#8220;sliding window.&#8221; This approach moves a window through the time series data, adding a sequence of multiple data points to the input data with each step. The target value (e.g., Closing Price) follows this sequence, and we store it in a separate target dataset. Then we push the window one step further and repeat these activities. This process results in a data set with many input sequences (mini-batches), each with a corresponding target value in the target record. This process applies both to the training and the test data.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="1943" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-4-5/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/06/image-4.png" data-orig-size="852,422" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-4" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/06/image-4.png" src="https://www.relataly.com/wp-content/uploads/2020/06/image-4.png" alt="Sliding window approach to partition multivariate data for time series forecasting" class="wp-image-1943" width="469" height="232" srcset="https://www.relataly.com/wp-content/uploads/2020/06/image-4.png 852w, https://www.relataly.com/wp-content/uploads/2020/06/image-4.png 300w, https://www.relataly.com/wp-content/uploads/2020/06/image-4.png 768w" sizes="(max-width: 469px) 100vw, 469px" /><figcaption class="wp-element-caption">Sliding Window</figcaption></figure>
</div>
</div>



<p>We will apply the sliding window approach to our data. The result is a training set (x_train) containing 2258 input sequences, each with 50 steps and six features. The related target dataset (y_train) has 2258 target values. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Set the sequence length - this is the timeframe used to make a single prediction
sequence_length = 50

# Prediction Index
index_Close = data.columns.get_loc(&quot;Close&quot;)

# Split the training data into train and train data sets
# As a first step, we get the number of rows to train the model on 80% of the data 
train_data_len = math.ceil(np_data_scaled.shape[0] * 0.8)

# Create the training and test data
train_data = np_data_scaled[0:train_data_len, :]
test_data = np_data_scaled[train_data_len - sequence_length:, :]

# The RNN needs data with the format of [samples, time steps, features]
# Here, we create N samples, sequence_length time steps per sample, and 6 features
def partition_dataset(sequence_length, data):
    x, y = [], []
    data_len = data.shape[0]
    for i in range(sequence_length, data_len):
        x.append(data[i-sequence_length:i,:]) #contains sequence_length values 0-sequence_length * columsn
        y.append(data[i, index_Close]) #contains the prediction values for validation,  for single-step prediction
    
    # Convert the x and y to numpy arrays
    x = np.array(x)
    y = np.array(y)
    return x, y

# Generate training data and test data
x_train, y_train = partition_dataset(sequence_length, train_data)
x_test, y_test = partition_dataset(sequence_length, test_data)

# Print the shapes: the result is: (rows, training_sequence, features) (prediction value, )
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

# Validate that the prediction value and the input match up
# The last close price of the second input sample should equal the first prediction value
print(x_train[1][sequence_length-1][index_Close])
print(y_train[0])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">(2474, 50, 5) (2474,)
(630, 50, 5) (630,)
0.02049456793614579
0.02049456793614579</pre></div>



<h3 class="wp-block-heading" id="h-step-5-train-the-multivariate-prediction-model">Step #5 Train the Multivariate Prediction Model</h3>



<p>Once we have the data prepared and ready, we can train our model. The architecture of our neural network consists of the following four layers:</p>



<ul class="wp-block-list">
<li>An LSTM layer, which takes our mini-batches as input and returns the whole sequence</li>



<li>Another LSTM layer that takes the sequence from the previous layer but only returns five values</li>



<li>Dense layer with five neurons</li>



<li>A final dense layer that outputs the predicted value</li>
</ul>



<p>The number of neurons in the first layer must equal the size of a minibatch of the input data. Each minibatch in our dataset consists of a matrix with 50 steps and six features. Thus, the input layer of our recurrent neural network consists of 300 neurons. Keeping this architecture in mind is essential because, later, we need to bring the data into the same shape when we want to predict a new dataset. Running the code below creates the model architecture and compiles the model.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Configure the neural network model
model = Sequential()

# Model with n_neurons = inputshape Timestamps, each with x_train.shape[2] variables
n_neurons = x_train.shape[1] * x_train.shape[2]
print(n_neurons, x_train.shape[1], x_train.shape[2])
model.add(LSTM(n_neurons, return_sequences=True, input_shape=(x_train.shape[1], x_train.shape[2]))) 
model.add(LSTM(n_neurons, return_sequences=False))
model.add(Dense(5))
model.add(Dense(1))

# Compile the model
model.compile(optimizer='adam', loss='mse')</pre></div>



<p>Running the code below starts the training process.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Training the model
epochs = 50
batch_size = 16
early_stop = EarlyStopping(monitor='loss', patience=5, verbose=1)
history = model.fit(x_train, y_train, 
                    batch_size=batch_size, 
                    epochs=epochs,
                    validation_data=(x_test, y_test)
                   )
                    
                    #callbacks=[early_stop])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Output exceeds the size limit. Open the full output data in a text editor
Epoch 1/50
155/155 [==============================] - 6s 19ms/step - loss: 6.7374e-04 - val_loss: 0.0011
Epoch 2/50
155/155 [==============================] - 2s 13ms/step - loss: 6.6207e-05 - val_loss: 8.4700e-04
Epoch 3/50
155/155 [==============================] - 2s 14ms/step - loss: 5.0667e-05 - val_loss: 6.6467e-04
Epoch 4/50
155/155 [==============================] - 2s 14ms/step - loss: 5.8446e-05 - val_loss: 6.8575e-04
Epoch 5/50
155/155 [==============================] - 2s 14ms/step - loss: 4.8430e-05 - val_loss: 8.4892e-04
Epoch 6/50
155/155 [==============================] - 2s 14ms/step - loss: 7.1283e-05 - val_loss: 8.2255e-04
Epoch 7/50
155/155 [==============================] - 2s 15ms/step - loss: 6.0554e-05 - val_loss: 8.0583e-04
Epoch 8/50
155/155 [==============================] - 2s 15ms/step - loss: 5.5977e-05 - val_loss: 4.5830e-04
Epoch 9/50
155/155 [==============================] - 2s 15ms/step - loss: 4.2453e-05 - val_loss: 6.1866e-04
Epoch 10/50
155/155 [==============================] - 2s 14ms/step - loss: 3.5722e-05 - val_loss: 4.5288e-04
Epoch 11/50
155/155 [==============================] - 2s 14ms/step - loss: 4.1409e-05 - val_loss: 8.5975e-04
Epoch 12/50
155/155 [==============================] - 3s 18ms/step - loss: 7.0007e-05 - val_loss: 5.0300e-04
Epoch 13/50
...
Epoch 49/50
155/155 [==============================] - 2s 13ms/step - loss: 2.7064e-05 - val_loss: 2.8202e-04
Epoch 50/50
155/155 [==============================] - 2s 13ms/step - loss: 2.9009e-05 - val_loss: 2.5486e-04</pre></div>



<p>Let&#8217;s take a quick look at the loss curve. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot training &amp; validation loss values
fig, ax = plt.subplots(figsize=(16, 5), sharex=True)
sns.lineplot(data=history.history[&quot;loss&quot;])
plt.title(&quot;Model loss&quot;)
plt.ylabel(&quot;Loss&quot;)
plt.xlabel(&quot;Epoch&quot;)
ax.xaxis.set_major_locator(plt.MaxNLocator(epochs))
plt.legend([&quot;Train&quot;, &quot;Test&quot;], loc=&quot;upper left&quot;)
plt.grid()
plt.show()</pre></div>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="521" data-attachment-id="5051" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/image-74-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2021/06/image-74.png" data-orig-size="1188,604" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-74" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2021/06/image-74.png" src="https://www.relataly.com/wp-content/uploads/2021/06/image-74-1024x521.png" alt="Loss curve after training the recurrent neural network for stock market prediction" class="wp-image-5051" srcset="https://www.relataly.com/wp-content/uploads/2021/06/image-74.png 1024w, https://www.relataly.com/wp-content/uploads/2021/06/image-74.png 300w, https://www.relataly.com/wp-content/uploads/2021/06/image-74.png 768w, https://www.relataly.com/wp-content/uploads/2021/06/image-74.png 1188w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The loss drops quickly to a lower plateau, which signals that the model has improved throughout the training process. </p>



<h3 class="wp-block-heading" id="h-step-6-evaluate-model-performance">Step #6 Evaluate Model Performance</h3>



<p>Once we have trained the neural network regression model, we want to measure its performance. As mentioned in section 3, we first have to reverse the scaling of the predictions. Afterward, we calculate different error metrics, MAE, MAPE, and MDAPE. Then we will compare the predictions in a line plot with the actual values. For more information on measuring the performance of regression models, see <a href="https://www.relataly.com/regression-error-metrics-python/923/" target="_blank" rel="noreferrer noopener">this relataly article</a>.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the predicted values
y_pred_scaled = model.predict(x_test)

# Unscale the predicted values
y_pred = scaler_pred.inverse_transform(y_pred_scaled)
y_test_unscaled = scaler_pred.inverse_transform(y_test.reshape(-1, 1))

# Mean Absolute Error (MAE)
MAE = mean_absolute_error(y_test_unscaled, y_pred)
print(f'Median Absolute Error (MAE): {np.round(MAE, 2)}')

# Mean Absolute Percentage Error (MAPE)
MAPE = np.mean((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled))) * 100
print(f'Mean Absolute Percentage Error (MAPE): {np.round(MAPE, 2)} %')

# Median Absolute Percentage Error (MDAPE)
MDAPE = np.median((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled)) ) * 100
print(f'Median Absolute Percentage Error (MDAPE): {np.round(MDAPE, 2)} %')</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Median Absolute Error (MAE): 175.28
Mean Absolute Percentage Error (MAPE): 1.48 %
Median Absolute Percentage Error (MDAPE): 1.16 %</pre></div>



<p>The MAPE is 22.15, which means that the mean of our predictions deviates from the actual values by 3.12%. The MDAPE is 2.88 % and a bit lower than the mean, thus indicating there are some outliers among the prediction errors. 50% of the predictions deviate by more than 2.88%, and 50% differ by less than 2.88% from the actual values.</p>



<p>Next, we create a line plot showing the forecast and compare it to the actual values. Adding a bar plot to the chart helps highlight the deviations of the predictions from the actual values. Running the code below creates the line plot.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># The date from which on the date is displayed
display_start_date = &quot;2019-01-01&quot; 

# Add the difference between the valid and predicted prices
train = pd.DataFrame(data_filtered_ext['Close'][:train_data_len + 1]).rename(columns={'Close': 'y_train'})
valid = pd.DataFrame(data_filtered_ext['Close'][train_data_len:]).rename(columns={'Close': 'y_test'})
valid.insert(1, &quot;y_pred&quot;, y_pred, True)
valid.insert(1, &quot;residuals&quot;, valid[&quot;y_pred&quot;] - valid[&quot;y_test&quot;], True)
df_union = pd.concat([train, valid])

# Zoom in to a closer timeframe
df_union_zoom = df_union[df_union.index &gt; display_start_date]

# Create the lineplot
fig, ax1 = plt.subplots(figsize=(16, 8))
plt.title(&quot;y_pred vs y_test&quot;)
plt.ylabel(stockname, fontsize=18)
sns.set_palette([&quot;#090364&quot;, &quot;#1960EF&quot;, &quot;#EF5919&quot;])
sns.lineplot(data=df_union_zoom[['y_pred', 'y_train', 'y_test']], linewidth=1.0, dashes=False, ax=ax1)

# Create the bar plot with the differences
df_sub = [&quot;#2BC97A&quot; if x &gt; 0 else &quot;#C92B2B&quot; for x in df_union_zoom[&quot;residuals&quot;].dropna()]
ax1.bar(height=df_union_zoom['residuals'].dropna(), x=df_union_zoom['residuals'].dropna().index, width=3, label='residuals', color=df_sub)
plt.legend()
plt.show()</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="964" height="492" data-attachment-id="8621" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/output-2-3/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/output-2.png" data-orig-size="964,492" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="output-2" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/output-2.png" src="https://www.relataly.com/wp-content/uploads/2022/05/output-2.png" alt="line plot that shows the stock market forecast that we have generated with the multivariate time series model, python tutorial" class="wp-image-8621" srcset="https://www.relataly.com/wp-content/uploads/2022/05/output-2.png 964w, https://www.relataly.com/wp-content/uploads/2022/05/output-2.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/output-2.png 768w" sizes="(max-width: 964px) 100vw, 964px" /></figure>



<p>The line plot shows that the forecast is close to the actual values but partially deviates from it. The deviations between actual values and predictions are called residuals. For our mode, they seem to be most significant during periods of increased market volatility and least during periods of steady market movement, which makes sense because sudden movements are generally more difficult to predict.</p>



<h3 class="wp-block-heading" id="h-step-7-predict-the-next-day-s-price">Step #7 Predict the Next Day&#8217;s Price</h3>



<p>After training the neural network, we want to forecast the stock market for the next day. For this purpose, we extract a new dataset from the Yahoo-Finance API and preprocess it as we did for model training. </p>



<p>We trained our model with mini-batches of 50-steps and six features. Thus, we must also provide the model with 50-steps when making the forecast. As before, we transform the data into the shape of 1 x 50 x 6, whereby the last figure is the number of feature columns. After generating the forecast, we unscale the stock market predictions back to the original range of values. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">df_temp = df[-sequence_length:]
new_df = df_temp.filter(FEATURES)

N = sequence_length

# Get the last N day closing price values and scale the data to be values between 0 and 1
last_N_days = new_df[-sequence_length:].values
last_N_days_scaled = scaler.transform(last_N_days)

# Create an empty list and Append past N days
X_test_new = []
X_test_new.append(last_N_days_scaled)

# Convert the X_test data set to a numpy array and reshape the data
pred_price_scaled = model.predict(np.array(X_test_new))
pred_price_unscaled = scaler_pred.inverse_transform(pred_price_scaled.reshape(-1, 1))

# Print last price and predicted price for the next day
price_today = np.round(new_df['Close'][-1], 2)
predicted_price = np.round(pred_price_unscaled.ravel()[0], 2)
change_percent = np.round(100 - (price_today * 100)/predicted_price, 2)

plus = '+'; minus = ''
print(f'The close price for {stockname} at {end_date} was {price_today}')
print(f'The predicted close price is {predicted_price} ({plus if change_percent &gt; 0 else minus}{change_percent}%)')</pre></div>



<p>The close price for NASDAQ on 2021-06-27 was 14360.39. The predicted closing price is 14232.8095703125 (-0.9%) </p>



<div style="height:31px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>This tutorial has shown multivariate time series modeling for stock market prediction in Python. We trained a neural network regression model for predicting the NASDAQ index. Before training our model, we performed several steps to prepare the data. The steps included splitting the data and scaling them. In addition, we created and tested various new features from the original time series data to account for the multivariate modeling approach. You now have the knowledge and code to conduct further experiments with the features of your choice.  </p>



<p>Multivariate time series forecasting is a complex topic. You might want to take the time to retrace the different steps. Especially the transformation of the data can be challenging. The best way to learn is to practice. Therefore I encourage you to develop more time series models and experiment with other data sources.</p>



<p>Another interesting approach to stock market prediction uses candlestick images and convolutional neural networks. If this topic interests you, check out the following article: <a href="http://deep%20reinforcement%20learning%20stock%20market%20trading%2C%20utilizing%20a%20cnn%20with%20candlestick%20imageshttps//journals.plos.org/plosone/article?id=10.1371/journal.pone.0263181" target="_blank" rel="noreferrer noopener">Deep reinforcement learning stock market trading, utilizing a CNN with candlestick images</a></p>



<p>I am always trying to learn and improve. If you want to give feedback or have remarks, feel free to share them in the comments.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large"><img decoding="async" width="512" height="512" data-attachment-id="12779" data-permalink="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png" data-orig-size="1024,1024" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="scatterplot python machine learning relataly midjourney lineplot financial data-min" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png" src="https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min-512x512.png" alt="Stockmarket forecasting with a neural network is about identifying meaningful patterns, but there is no guarantee that these patterns are present. Image created with Midjourney.  " class="wp-image-12779" srcset="https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png 512w, https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png 300w, https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png 140w, https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png 768w, https://www.relataly.com/wp-content/uploads/2020/06/scatterplot-python-machine-learning-relataly-midjourney-lineplot-financial-data-min.png 1024w" sizes="(max-width: 512px) 100vw, 512px" /><figcaption class="wp-element-caption">Stockmarket forecasting with a neural network is about identifying meaningful patterns. Be aware that there is no guarantee that these patterns are present in the data. Image created with <a href="http://www.midjourney.com" target="_blank" rel="noreferrer noopener">Midjourney</a>.  </figcaption></figure>
</div>
</div>



<h2 class="wp-block-heading" id="h-sources-and-further-reading">Sources and Further Reading</h2>



<ol class="wp-block-list"><li><a href="https://amzn.to/3MyU6Tj" target="_blank" rel="noreferrer noopener">Charu C. Aggarwal (2018) Neural Networks and Deep Learning</a></li><li><a href="https://amzn.to/3yIQdWi" target="_blank" rel="noreferrer noopener">Jansen (2020) Machine Learning for Algorithmic Trading: Predictive models to extract signals from market and alternative data for systematic trading strategies with Python</a></li><li><a href="https://amzn.to/3S9Nfkl" target="_blank" rel="noreferrer noopener">Aurélien Géron (2019) Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems </a></li><li><a href="https://amzn.to/3EKidwE" target="_blank" rel="noreferrer noopener">David Forsyth (2019) Applied Machine Learning Springer</a></li><li><a href="https://amzn.to/3MAy8j5" target="_blank" rel="noreferrer noopener">Andriy Burkov (2020) Machine Learning Engineering</a></li></ol>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>
<p>The post <a href="https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/">Stock Market Prediction using Multivariate Time Series and Recurrent Neural Networks in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/stock-market-prediction-using-multivariate-time-series-in-python/1815/feed/</wfw:commentRss>
			<slash:comments>17</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1815</post-id>	</item>
		<item>
		<title>Measuring Regression Errors with Python</title>
		<link>https://www.relataly.com/regression-error-metrics-python/923/</link>
					<comments>https://www.relataly.com/regression-error-metrics-python/923/#comments</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Mon, 04 May 2020 09:34:09 +0000</pubDate>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Data Science]]></category>
		<category><![CDATA[Measuring Model Performance]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Synthetic Data]]></category>
		<category><![CDATA[Time Series Forecasting]]></category>
		<category><![CDATA[Beginner Tutorials]]></category>
		<category><![CDATA[Regression Error Metrics]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=923</guid>

					<description><![CDATA[<p>Evaluating performance is a crucial step in developing regression models. Because regression models return continuous outputs, such models allow for different gradations of right or wrong. Therefore, we measure the deviation between predictions and actual values in numerical terms. However, a universal metric to measure the performance of regression models does not exist. Instead, there ... <a title="Measuring Regression Errors with Python" class="read-more" href="https://www.relataly.com/regression-error-metrics-python/923/" aria-label="Read more about Measuring Regression Errors with Python">Read more</a></p>
<p>The post <a href="https://www.relataly.com/regression-error-metrics-python/923/">Measuring Regression Errors with Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Evaluating performance is a crucial step in developing regression models. Because regression models return continuous outputs, such models allow for different gradations of right or wrong. Therefore, we measure the deviation between predictions and actual values in numerical terms. However, a universal metric to measure the performance of regression models does not exist. Instead, there are several metrics, each with its advantages and disadvantages. None of these metrics is sufficient alone, and it is often necessary to use them in combination. This article presents six regression error metrics and explains how to implement them in Python with Scikit-learn.</p>



<p>The rest of this article proceeds in two parts. The first part is conceptual and introduces six error metrics for measuring regression performance. We look at formulas and discuss their pros and cons. The discussion is summarized in a cheat sheet. The second part is a hands-on Python tutorial in which we generate synthetic time series data and use them for training a prediction model. Then we implement the six regression error metrics and evaluate the performance of our model.</p>



<p>Note that this article deals with regression errors. If you are looking for an overview of classification error metrics, check out this tutorial on <a href="https://www.relataly.com/measuring-classification-performance-with-python-and-scikit-learn/846/" target="_blank" rel="noreferrer noopener">classification error metrics</a>.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"><div class="wp-block-image">
<figure class="alignright size-full"><img decoding="async" width="504" height="658" data-attachment-id="12494" data-permalink="https://www.relataly.com/business-use-cases-for-openai-gpt-models-chatgpt-davinci/12200/goal-arrow-shot-archery-machine-learning-error-metrics/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/02/goal-arrow-shot-archery-machine-learning-error-metrics.png" data-orig-size="504,658" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="goal arrow shot archery machine learning error metrics" data-image-description="&lt;p&gt;goal arrow shot archery machine learning error metrics&lt;/p&gt;
" data-image-caption="&lt;p&gt;goal arrow shot archery machine learning error metrics&lt;/p&gt;
" data-large-file="https://www.relataly.com/wp-content/uploads/2023/02/goal-arrow-shot-archery-machine-learning-error-metrics.png" src="https://www.relataly.com/wp-content/uploads/2023/02/goal-arrow-shot-archery-machine-learning-error-metrics.png" alt="goal arrow shot archery machine learning error metrics" class="wp-image-12494" srcset="https://www.relataly.com/wp-content/uploads/2023/02/goal-arrow-shot-archery-machine-learning-error-metrics.png 504w, https://www.relataly.com/wp-content/uploads/2023/02/goal-arrow-shot-archery-machine-learning-error-metrics.png 230w" sizes="(max-width: 504px) 100vw, 504px" /><figcaption class="wp-element-caption">goal arrow shot archery machine learning error metrics</figcaption></figure>
</div></div>
</div>



<h2 class="wp-block-heading" id="h-measuring-regression-errors">Measuring Regression Errors</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In general, we measure the performance of regression models by calculating the deviations between the predictions (y_pred) and the actual values (y_test). If the prediction value is below the actual value, the prediction error is positive. If the prediction lies above the real value, the prediction error is negative. However, in a sample of prediction values, the errors can vary greatly depending on the data point. Therefore, it is not enough to look at individual error values. Error metrics can inform us about the statistical distribution of errors in a prediction sample and, in this way, help us to measure the performance of regression models objectively.</p>



<p>Various metrics exist to measure regression errors. Each error metric can only cover a part of the overall picture. For instance, imagine you have developed a model to predict the consumption of a power plant. The model predictions are generally accurate, but the projections are wrong in a few cases. In other words, outliers among the prediction errors make it difficult to conclude the model performance. It is insufficient to calculate the average prediction error to understand this situation. Instead, a more robust measuring approach would combine different error metrics to conclude the probability that prediction errors lie within a specific range.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="979" data-permalink="https://www.relataly.com/image-34-2/" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/image-34.png" data-orig-size="1134,596" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-34" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/image-34.png" src="https://www.relataly.com/wp-content/uploads/2020/05/image-34-1024x538.png" alt="Time Series Forecasting, measuring regression errors" class="wp-image-979" width="757" height="397" srcset="https://www.relataly.com/wp-content/uploads/2020/05/image-34.png 1024w, https://www.relataly.com/wp-content/uploads/2020/05/image-34.png 300w, https://www.relataly.com/wp-content/uploads/2020/05/image-34.png 768w, https://www.relataly.com/wp-content/uploads/2020/05/image-34.png 1134w" sizes="(max-width: 757px) 100vw, 757px" /><figcaption class="wp-element-caption">Predictions vs. Actual Values in Time Series Forecasting</figcaption></figure>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-six-error-metrics-for-measuring-regression-errors">Six Error Metrics for Measuring Regression Errors</h3>



<p>The following six metrics help measure prediction errors. We can apply them to various regression problems, including time series forecasting.</p>



<ul class="wp-block-list">
<li>Mean Absolute Error (MAE)</li>



<li>Mean Absolute Percentage Error (MAPE)</li>



<li>Median Absolute Error (MedAE)</li>



<li>Mean Squared Error (MSE)</li>



<li>Root Mean Squared Error (RMSE)</li>



<li>Median Absolute Percent Error (MdAPE)</li>
</ul>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-mean-absolute-error-mae">Mean Absolute Error (MAE)</h3>



<p>Mean Absolute Error (MAE) is a metric commonly used to measure the arithmetic average of deviations between predictions and actual values. </p>



<p>An MAE of &#8220;5&#8221; tells us that, on average, our predictions deviate from the actual values by 5. Whether this error is considered small or large will depend on the application case and the scale of the predictions. For instance, 5 nanometers in the case of a building might be small, but if it&#8217;s five nanometers in the case of a biological membrane, it might be significant. So when working with the MAE, mind the scale.</p>



<ul class="wp-block-list">
<li>It is scale-dependent</li>



<li>The MAE considers the absolute values to take both positive and negative deviations from the actual.</li>



<li>The MAE is sensitive to outliers, as large values can substantially impact. For this reason, we should use the MAE in combination with additional metrics.</li>



<li>The MAE shares the same unit with the predictions.</li>
</ul>



<div class="wp-block-mathml-mathmlblock"></div>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock">\[MAE=\ \frac{\sum_{i=1}^{n}{|y_i-x_i|}}{n}  \]<script src="https://www.relataly.com/wp-includes/js/dist/hooks.min.js?ver=dd5603f07f9220ed27f1" id="wp-hooks-js"></script>
<script src="https://www.relataly.com/wp-includes/js/dist/i18n.min.js?ver=c26c3dc7bed366793375" id="wp-i18n-js"></script>
<script id="wp-i18n-js-after">
wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } );
//# sourceURL=wp-i18n-js-after
</script>
<script  async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML" id="mathjax-js"></script>
</div>



<div class="wp-block-mathml-mathmlblock">\[x_i\ = actual value \\
y_i = predictions \\
n = sample size
\]

</div>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-mean-absolute-percentage-error-mape">Mean Absolute Percentage Error (MAPE)</h3>



<p>The mean absolute percentage error calculates the mean percentage deviation between predictions and actual values. </p>



<ul class="wp-block-list">
<li>The mean absolute percentage error is scale-independent, making it easier to interpret.</li>



<li>We must not use the MAPE whenever a single value is zero</li>



<li>The MAPE puts a heavier penalty on negative errors</li>
</ul>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock"></div>



<div class="wp-block-mathml-mathmlblock">\[\mathrm{MAPE=\ }\frac{\mathrm{1}}{\mathrm{n}}\sum_{\mathrm{t=1}}^{\mathrm{n}}\left|\frac{\mathrm{A}_\mathrm{t}\mathrm{\ -\ } \mathrm{F}_\mathrm{t}}{\mathrm{A}_\mathrm{t}}\right|\mathrm{\ \ast100}   \]</div>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-mean-squared-error-mse">Mean Squared Error (MSE)</h3>



<p>We can calculate the MSE by measuring the average squares of the differences between the estimated and actual values. </p>



<ul class="wp-block-list">
<li>Since all values are squared, the MSE is very sensitive to outliers.</li>



<li>An MSE much larger than the MAE indicates strong outliers among the prediction errors. The formula of the MSE is:</li>
</ul>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock"></div>



<div class="wp-block-mathml-mathmlblock">
\[MSE=\frac{\sum_{i=1}^{n}{|y_i-x_i|}^2}{n}
\]</div>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-median-absolute-error-medae">Median Absolute Error (MedAE)</h3>



<p>The Median Absolute Error (MedAE) calculates the median deviation between predictions and actual values.</p>



<ul class="wp-block-list">
<li>The MedAE has the same unit as the predictions.</li>



<li>A MedAE of value 10 means that 50% of the errors are greater than 10 and 50% are below this value.</li>



<li>The MedAE is resistant to outliers. Therefore, we often use it in combination with the MAE. A substantial deviation between MAE and MedAE is an indication that there are outliers among the errors. In other words, the prediction model deviates more from the actual value than on average.</li>
</ul>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock"></div>



<div class="wp-block-mathml-mathmlblock">\[MedAE=\frac{\sum_{i=1}^{n}{|y_i-{\hat{x}}_i|}}{n}\]</div>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-root-mean-squared-error-rmse">Root Mean Squared Error (RMSE)</h3>



<p>The root-mean-squared error is another standard way to measure the performance of a forecasting model.</p>



<ul class="wp-block-list">
<li>Has the same unit as the predictions </li>



<li>A good measure of how accurately the model predicts the response</li>



<li>Robust to outliers</li>
</ul>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock"></div>



<div class="wp-block-mathml-mathmlblock">\[RMSE=\frac{1}{n}\sqrt{\sum_{i=1}^{n}{|y_i-x_i|}^2}\]</div>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h3 class="wp-block-heading" id="h-median-absolute-percentage-error-mdape">Median Absolute Percentage Error (MdAPE)</h3>



<p>The median absolute percentage error (MdAPE) measures the accuracy of a prediction model.&nbsp;It is similar to the median absolute percentage error but, as the name implies, calculates the median error for a set of forecasts. As a result, the MdAPE is more resilient to outliers than the MAPE. However, it is also less intuitive. A MdAPE of 5% means that half of the absolute percentage errors are less than 5%, and half are over 5%.</p>



<ul class="wp-block-list">
<li>Scale-dependent </li>



<li>Not to be used whenever a single value is zero.</li>



<li>More robust to distortion from outliers than the MAPE</li>
</ul>
</div>



<div class="wp-block-column is-vertically-aligned-center has-base-background-color has-background is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<div class="wp-block-mathml-mathmlblock">\[MdAPE=
\ median(\left|\frac{A_t\ -\ F_t}{A_t}\right|)\ast100\]</div>
</div>
</div>



<h3 class="wp-block-heading" id="h-regression-error-cheat-sheet">Regression Error Cheat Sheet</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p>The cheat sheet provides an overview of the six regression error metrics. It contains the mathematical formula for each regression metric, a short code sequence to implement in Python, and some hints for their interpretation.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-file"><a id="wp-block-file--media-aa9f2dfb-6165-46c9-b04d-616260f6ec5b">Cheat-Sheet.pdf</a><a href="https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-Error-Metrics-in-Time-Series-Forecasting-4.pdf" class="wp-block-file__button wp-element-button" download aria-describedby="wp-block-file--media-aa9f2dfb-6165-46c9-b04d-616260f6ec5b">Download</a></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="1038" data-permalink="https://www.relataly.com/cheat-sheet-7/" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-7.png" data-orig-size="549,836" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Cheat-Sheet-7" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-7.png" src="https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-7.png" alt="Python regression cheat sheet" class="wp-image-1038" width="135" height="205" srcset="https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-7.png 549w, https://www.relataly.com/wp-content/uploads/2020/05/Cheat-Sheet-7.png 197w" sizes="(max-width: 135px) 100vw, 135px" /></figure>
</div>
</div>
</div>
</div>



<div style="height:34px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<h2 class="wp-block-heading" id="h-implementing-regression-error-metrics-in-python-time-series-prediction-example">Implementing Regression Error Metrics in Python: Time Series Prediction Example</h2>



<p>Now that we have familiarized ourselves with standard regression error metrics, it&#8217;s time to see them in action. In the following, we will develop and test a regression model in Python. We begin by generating some synthetic time series data. Subsequently, we use the data to train a simple regression model based on a Keras neural network. The model will try to continue the time series and predicts a continious value for the next time step. We will use this model to predict a test dataset and measure the prediction performance using the error metrics. </p>



<p>The code of this Python example is available on the GitHub repository.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_e8e26f-dc"><a class="kb-button kt-button button kb-btn_209df1-3f kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/01%20Time%20Series%20Forecasting%20%26%20Regression/009%20Measuring%20Regression%20Model%20Performance.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_6931ed-04 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly Github Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p>Before starting the coding part, make sure that you have set up your <a href="https://www.python.org/downloads/" target="_blank" rel="noreferrer noopener">Python 3</a> environment and required packages. If you don&#8217;t have an environment yet, you can follow the steps in&nbsp;<a href="https://www.relataly.com/anaconda-python-environment-machine-learning/1663/" target="_blank" rel="noreferrer noopener">this tutorial</a>&nbsp;to set up the&nbsp;<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda environment</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><a href="https://docs.python.org/3/library/math.html" target="_blank" rel="noreferrer noopener">math</a></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>



<li><a href="https://seaborn.pydata.org/" target="_blank" rel="noreferrer noopener">Seaborn</a></li>
</ul>



<p>In addition, we will be using <em><a href="https://keras.io/" target="_blank" rel="noreferrer noopener">Keras&nbsp;</a></em>(2.0 or higher) with <a href="https://www.tensorflow.org/" target="_blank" rel="noreferrer noopener"><em>Tensorflow</em> </a>backend and the machine learning library <a href="https://scikit-learn.org/stable/" target="_blank" rel="noreferrer noopener">scikit-learn</a>.</p>



<p>You can install packages using console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>



<h3 class="wp-block-heading" id="h-step-1-generate-synthetic-time-series-data">Step #1 Generate Synthetic Time Series Data</h3>



<p>We begin by generating synthetic time series data. The script below creates the time series by multiplying different sine curves. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># A tutorial for this file is available at www.relataly.com

import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl 
from tensorflow.keras.models import Sequential
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.layers import LSTM, Dense
import seaborn as sns
sns.set_style('white', { 'axes.spines.right': False, 'axes.spines.top': False})

# Creating the sample sinus curve dataset
steps = 1000; gradient = 0.002
list_a = []
for i in range(0, steps, 1):
    y = 100 * round(math.sin(math.pi * i * 0.02 + 0.01), 4) * round(math.sin(math.pi * i * 0.005 + 0.01), 4) * round(math.sin(math.pi * i * 0.005 + 0.01), 4)
    list_a.append(y)
df = pd.DataFrame({&quot;valid&quot;: list_a}, columns=[&quot;valid&quot;])

# Visualizing the data
fig, ax1 = plt.subplots(figsize=(16, 4))
sns.lineplot(data=df)
ax1.xaxis.set_major_locator(plt.MaxNLocator(30))
plt.title(&quot;sine curve data&quot;)</pre></div>



<figure class="wp-block-image size-full"><img decoding="async" width="940" height="264" data-attachment-id="8128" data-permalink="https://www.relataly.com/regression-error-metrics-python/923/sine-curve-synthetic-data/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png" data-orig-size="940,264" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sine-curve-synthetic-data" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png" src="https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png" alt="synthetic sine curve data, measuring model performance, time series regression, regression errors" class="wp-image-8128" srcset="https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png 940w, https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/sine-curve-synthetic-data.png 768w" sizes="(max-width: 940px) 100vw, 940px" /></figure>



<h3 class="wp-block-heading" id="h-step-2-data-preparation">Step #2 Data Preparation </h3>



<p>Now that we have the synthetic data available, we can prepare it as inputs for training our regression model. Running the following code will scale and split the data and bring it into a shape that we can use as input batches to a neural network.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Feature Selection - Only Close Data
train_df = df.copy()
data_unscaled = df.values

# Transform features by scaling each feature to a range between 0 and 1
mmscaler = MinMaxScaler(feature_range=(0, 1))
np_data = mmscaler.fit_transform(data_unscaled)

# Set the sequence length - this is the timeframe used to make a single prediction
sequence_length = 15

# Prediction Index
index_Close = 0

# Split the training data into train and train data sets
# As a first step, we get the number of rows to train the model on 80% of the data 
train_data_length = math.ceil(np_data.shape[0] * 0.8)

# Create the training and test data
train_data = np_data[0:train_data_length, :]
test_data = np_data[train_data_length - sequence_length:, :]

# The RNN needs data with the format of [samples, time steps, features]
# Here, we create N samples, sequence_length time steps per sample, and 6 features
def partition_dataset(sequence_length, train_df):
    x, y = [], []
    data_len = train_df.shape[0]
    for i in range(sequence_length, data_len):
        x.append(train_df[i-sequence_length:i,:]) #contains sequence_length values 0-sequence_length * columsn
        y.append(train_df[i, index_Close]) #contains the prediction values for validation (3rd column = Close),  for single-step prediction
    
    # Convert the x and y to numpy arrays
    x = np.array(x)
    y = np.array(y)
    return x, y

# Generate training data and test data
x_train, y_train = partition_dataset(sequence_length, train_data)
x_test, y_test = partition_dataset(sequence_length, test_data)

# Print the shapes: the result is: (rows, training_sequence, features) (prediction value, )
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

# Validate that the prediction value and the input match up
# The last close price of the second input sample should equal the first prediction value
print(x_test[1][sequence_length-1][index_Close])
print(y_test[0])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Out: x_tain.shape: (584, 15, 1) -- y_tain.shape: (584,) </pre></div>



<h3 class="wp-block-heading" id="h-step-3-training-a-time-series-regression-model">Step #3 Training a Time Series Regression Model </h3>



<p>Once we have prepared the data, we can train the regression model. Our model uses a simple neural network architecture. Running the code below defines the model architecture and compiles the model. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Settings
batch_size = 5
epochs = 4
n_features = 1

# Configure and compile the neural network model
# The number of input neurons is defined by the sequence length multiplied by the number of features
lstm_neuron_number = sequence_length * n_features

# Create the model
model = Sequential()
model.add(LSTM(lstm_neuron_number, return_sequences=False, input_shape=(x_train.shape[1], 1))
)
model.add(Dense(1))
model.compile(optimizer=&quot;adam&quot;, loss=&quot;mean_squared_error&quot;)

# Train the model
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs)</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Epoch 1/4 
584/584 [==============================] - 1s 2ms/step - loss: 0.1047 Epoch 2/4 
584/584 [==============================] - 1s 1ms/step - loss: 0.0153 Epoch 3/4 
584/584 [==============================] - 1s 1ms/step - loss: 0.0102 Epoch 4/4 
584/584 [==============================] - 1s 1ms/step - loss: 0.0064</pre></div>



<p>Now that the model architecture is defined, you can run the code below to initiate the training process. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Settings
batch_size = 5

# Train the model
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs)</pre></div>



<h3 class="wp-block-heading" id="h-step-4-making-test-predictions">Step #4 Making Test Predictions</h3>



<p>Let&#8217;s see how good or bad our model performs. We will make predictions on our test dataset by running the code below. We store the results in a new DataFrame called &#8220;predictions.&#8221;</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Get the predicted values
y_pred_scaled = model.predict(x_test)
y_pred = mmscaler.inverse_transform(y_pred_scaled)
y_test_unscaled = mmscaler.inverse_transform(y_test.reshape(-1, 1))</pre></div>



<p>Next, we want to get an idea of how our model performs. We, therefore, create a line plot that shows the predictions and the actual values of the time series. We colorize the differences between predictions and actual values to highlight the prediction errors. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Create the line plot
test_df = pd.DataFrame({'y_test': y_test_unscaled.flatten(), 'y_pred': y_pred.flatten()})
fig, ax1 = plt.subplots(figsize=(16, 8), sharex=True)
sns.lineplot(data=test_df)
ax1.tick_params(axis=&quot;x&quot;, rotation=0, labelsize=10, length=0)
plt.title(&quot;y_pred vs y_test Truth&quot;)
plt.legend([&quot;y_pred&quot;, &quot;y_test&quot;], loc=&quot;upper left&quot;)

# Fill between plotlines
mpl.rc('hatch', color='k', linewidth=2)
ax1.fill_between(test_df.index, test_df[&quot;y_test&quot;], test_df[&quot;y_pred&quot;],  facecolor = 'white', alpha=.9) 
plt.show()</pre></div>



<figure class="wp-block-image size-large"><img decoding="async" width="935" height="482" data-attachment-id="1856" data-permalink="https://www.relataly.com/regression-error-metrics-python/923/image-60-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/image-60.png" data-orig-size="935,482" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-60" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/image-60.png" src="https://www.relataly.com/wp-content/uploads/2020/05/image-60.png" alt="line plot on the forecasted data, time series regression errors, error metrics, keras, python, tutorial, measuring model performance machine learning" class="wp-image-1856" srcset="https://www.relataly.com/wp-content/uploads/2020/05/image-60.png 935w, https://www.relataly.com/wp-content/uploads/2020/05/image-60.png 300w, https://www.relataly.com/wp-content/uploads/2020/05/image-60.png 768w" sizes="(max-width: 935px) 100vw, 935px" /></figure>



<p>The plot shows that the prediction errors vary and are sometimes positive and sometimes negative. </p>



<h3 class="wp-block-heading" id="h-step-5-calculating-the-regression-error-metrics-implementation-and-evaluation">Step #5 Calculating the Regression Error Metrics: Implementation and Evaluation</h3>



<p>Now that we have predicted the test set, we calculate the six regression error metrics. In most cases, you won&#8217;t have to use all six regression error metrics to understand how well a model performs. In most cases, it is sufficient to use a combination of two or three of them. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Mean Absolute Error (MAE)
MAE = np.mean(abs(y_pred - y_test_unscaled))
print('Mean Absolute Error (MAE): ' + str(np.round(MAE, 2)))

# Median Absolute Error (MedAE)
MEDAE = np.median(abs(y_pred - y_test_unscaled))
print('Median Absolute Error (MedAE): ' + str(np.round(MEDAE, 2)))

# Mean Squared Error (MSE)
MSE = np.square(np.subtract(y_pred, y_test_unscaled)).mean()
print('Mean Squared Error (MSE): ' + str(np.round(MSE, 2)))

# Root Mean Squarred Error (RMSE) 
RMSE = np.sqrt(np.mean(np.square(y_pred - y_test_unscaled)))
print('Root Mean Squared Error (RMSE): ' + str(np.round(RMSE, 2)))

# Mean Absolute Percentage Error (MAPE)
MAPE = np.mean((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled))) * 100
print('Mean Absolute Percentage Error (MAPE): ' + str(np.round(MAPE, 2)) + ' %')

# Median Absolute Percentage Error (MDAPE)
MDAPE = np.median((np.abs(np.subtract(y_test_unscaled, y_pred)/ y_test_unscaled))) * 100
print('Median Absolute Percentage Error (MDAPE): ' + str(np.round(MDAPE, 2)) + ' %')</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Mean Absolute Error (MAE): 6.95 
Median Absolute Error (MedAE): 5.05  
Mean Squared Error (MSE): 78.7 
Root Mean Squared Error (RMSE): 8.87  
Mean Absolute Percentage Error (MAPE): 10339.13 % 
Median Absolute Percentage Error (MDAPE): 26.8 %</pre></div>



<h3 class="wp-block-heading">Step #6 Interpreting the Regression Error Metrics</h3>



<p>Let&#8217;s look at the regression error metrics, starting with the MAE and the MedAE. The MAE is 6.95, and the MedAE is 5.05. These values are close, indicating that our prediction errors are equally distributed but might include some outliers.</p>



<p>To better understand possible outliers, we look at the MSE. With a value of 78.7, the MAE is a little bit higher than the square of the MAE. The RMSE is slightly higher than the MAE, which is another indication that the prediction errors lie in a narrow range. </p>



<p>How much deviate the predictions of our model from the actual values in percentage terms? The MAPE is typically used as a starting point to answer this question. With 10339.13 percent, it is exceptionally high. So is our model very much mistaken? The answer is no &#8211; the MAPE is misleading. The problem is that several actual values are close to zero, e.g., 0.00001. While the predictions of our model are close to the actual values in absolute numbers, the MAPE divides the residual values by the actual values, e.g., 0.000001, and sums them up. Thus the MAPE becomes very large. </p>



<p>The Median of the MDAPE is 26.8%. So, 50% of our forecasting errors are higher than 26.8%, and 50% are lower. Consequently, we can assume that when our model makes a prediction, the probability that the deviation is 26.8% from the actual value is 50% &#8211; that is not as terrible as the MAPE would indicate. The plotlines of the predictions and actual values support these findings.</p>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>This article has presented six error metrics for evaluating regression errors. Remember that none of these metrics alone is sufficient to evaluate a model&#8217;s performance. Instead, we should use a combination of multiple metrics. We have discussed the advantages and disadvantages of the metrics. In the second part of this tutorial, we implemented a time series regression example. After training an exemplary regression model, we used the six regression metrics to evaluate the model performance. </p>



<p>It&#8217;s important to remember that different error metrics are suitable for different types of regression problems. For example, mean absolute error (MAE) is a good choice when you want to know how close the predictions are to the true values, but it is not very sensitive to large errors. Root mean squared error (RMSE) is a more sensitive metric that punishes large errors more heavily, but it can be difficult to interpret because it is in the same units as the original data.</p>



<p>I hope this article was helpful. If you have any remarks or questions remaining, write them in the comments. </p>



<h2 class="wp-block-heading">Sources and Further Reading</h2>



<div style="display: inline-block;">
  <iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=3030181162&amp;asins=3030181162&amp;linkId=669e46025028259138fbb5ccec12dfbe&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1999579577&amp;asins=1999579577&amp;linkId=91d862698bf9010ff4c09539e4c49bf4&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1839217715&amp;asins=1839217715&amp;linkId=356ba074068849ff54393f527190825d&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1492032646&amp;asins=1492032646&amp;linkId=2214804dd039e7103577abd08722abac&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
</div>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>
<p>The post <a href="https://www.relataly.com/regression-error-metrics-python/923/">Measuring Regression Errors with Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/regression-error-metrics-python/923/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">923</post-id>	</item>
		<item>
		<title>Rolling Time Series Forecasting: Creating a Multi-Step Prediction for a Rising Sine Curve using Neural Networks in Python</title>
		<link>https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/</link>
					<comments>https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/#comments</comments>
		
		<dc:creator><![CDATA[Florian Follonier]]></dc:creator>
		<pubDate>Sun, 19 Apr 2020 09:59:49 +0000</pubDate>
				<category><![CDATA[Algorithms]]></category>
		<category><![CDATA[Data Science]]></category>
		<category><![CDATA[Keras]]></category>
		<category><![CDATA[Neural Networks]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Recurrent Neural Networks]]></category>
		<category><![CDATA[Synthetic Data]]></category>
		<category><![CDATA[Tensorflow]]></category>
		<category><![CDATA[Time Series Forecasting]]></category>
		<category><![CDATA[AI in Finance]]></category>
		<category><![CDATA[Deep Learning]]></category>
		<category><![CDATA[Intermediate Tutorials]]></category>
		<category><![CDATA[Multi-Step Time Series Forecasting]]></category>
		<category><![CDATA[Multivariate Models]]></category>
		<category><![CDATA[Rolling Time Series Forecasting]]></category>
		<category><![CDATA[Supervised Learning]]></category>
		<guid isPermaLink="false">https://www.relataly.com/?p=275</guid>

					<description><![CDATA[<p>Many time forecasting problems can be solved by predicting just one step into the future. However, some problems require a forecast for an extended period of time, which calls for a multi-step time series forecasting approach. This approach involves modeling the distribution of future values of a signal over a prediction horizon. In this article, ... <a title="Rolling Time Series Forecasting: Creating a Multi-Step Prediction for a Rising Sine Curve using Neural Networks in Python" class="read-more" href="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/" aria-label="Read more about Rolling Time Series Forecasting: Creating a Multi-Step Prediction for a Rising Sine Curve using Neural Networks in Python">Read more</a></p>
<p>The post <a href="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/">Rolling Time Series Forecasting: Creating a Multi-Step Prediction for a Rising Sine Curve using Neural Networks in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Many time forecasting problems can be solved by predicting just one step into the future. However, some problems require a forecast for an extended period of time, which calls for a multi-step time series forecasting approach. This approach involves modeling the distribution of future values of a signal over a prediction horizon. In this article, we use the rising sine curve as an example to demonstrate how to apply a multi-step prediction approach using Keras neural networks with LSTM layers in Python. We create a rolling forecast for the sine curve by generating several single-step predictions and iteratively using them as input to predict further steps in the future.</p>



<p>The remainder of this article proceeds as follows: We begin by looking at the sine curve problem and provide a quick intro to recurrent neural networks. After this conceptual introduction, we turn to the hands-on part in Python. We generate synthetic sine curve data and preprocess the data to train univariate models using a Keras neural network. We thereby experiment with different architectures and hyperparameters. Then, we use these model variants to create rolling multi-step forecasts. Finally, we evaluate the model performance.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image is-resized"><img decoding="async" src="https://www.relataly.com/wp-content/uploads/2020/05/image-21.png" alt="A multi-step time series forecast for a rising sine curve, as we will create it in this article" width="357" height="122"/><figcaption class="wp-element-caption">A multi-step time series forecast for a rising sine curve, as we will create in this article.</figcaption></figure>



<p>If you are just getting started with time-series forecasting, we have covered the single-step forecasting approach in two previous articles: </p>



<p><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/003%20Time%20Series%20Forecasting%20-%20Univariate%20Model%20using%20Recurrent%20Neural%20Networks.ipynb" target="_blank" rel="noreferrer noopener">Predicting Stock Markets with Neural Networks &#8211; A Step-by-Step Guide</a><br><a href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/004%20Time%20Series%20Forecasting%20-%20Adjusting%20Prediction%20Intervals.ipynb" target="_blank" rel="noreferrer noopener">Stock Market Prediction – Adjusting Time Series Prediction Intervals in Python</a></p>
</div>
</div>



<h2 class="wp-block-heading" id="h-the-problem-of-a-rising-sine-curve">The Problem of a Rising Sine Curve</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The line plot below illustrates the sample of a rising sine curve. The goal is to use the ground truth values (blue) to forecast several points in this curve (purple).</p>



<p>Traditional mathematical methods can resolve this function by decomposing it into constituent parts. Thus, we could easily foresee the further course of the curve. But in practice, the function might not be precisely periodic and change over a more extended period. Therefore, recurrent neural networks can achieve better results than traditional mathematical approaches, especially when they train on extensive data.</p>



<p>Also: <a href="https://www.relataly.com/univariate-stock-market-forecasting-using-a-recurrent-neural-network/122/">Stock Market Prediction using Univariate Recurrent Neural Networks (RNN) with Python</a> </p>



<h2 class="wp-block-heading">Application Domains with Similar Time Series Forecasting Problems</h2>



<p>A rising sine wave may sound like an abstract problem at first. However, similar issues are widespread. Imagine you are the owner of an online shop. The users who visit your shop and buy something fluctuate depending on the time and the weekday. For instance, there are fewer visitors at night, and on weekends, the number of visitors rises sharply. At the same time, the overall number of users increases over a more extended period as the shop becomes known to a broader audience. To plan the number of goods to be held in stock, you need to see the number of orders at any point in time over several weeks. It is a typical multi-step time series problem, and similar problems exist in various domains:</p>



<ul class="wp-block-list">
<li>Healthcare: e.g., forecasting of health signals such as heart, blood, or breathing signals</li>



<li>Network Security: e.g., analysis of network traffic in intrusion detection systems</li>



<li>Sales and Marketing: e.g., forecasting of market demand</li>



<li>Production demand forecasting: e.g., for power consumption and capacity planning</li>



<li>Prediction and filtering of sensor signals, e.g., of audio signals</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="942" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-21-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/image-21.png" data-orig-size="938,319" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-21" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/image-21.png" src="https://www.relataly.com/wp-content/uploads/2020/05/image-21.png" alt="A time series forecasting problem: predicting sine curve data, Rolling forecasting" class="wp-image-942" width="375" height="128" srcset="https://www.relataly.com/wp-content/uploads/2020/05/image-21.png 938w, https://www.relataly.com/wp-content/uploads/2020/05/image-21.png 300w, https://www.relataly.com/wp-content/uploads/2020/05/image-21.png 768w" sizes="(max-width: 375px) 100vw, 375px" /><figcaption class="wp-element-caption">A time series forecasting problem: predicting sine curve data</figcaption></figure>
</div>
</div>



<h2 class="wp-block-heading" id="h-recurrent-neural-networks-for-time-series-forecasting">Recurrent Neural Networks for Time Series Forecasting</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>The model used in this article is a recurrent neural network with Long short-term memory (LSTM) layers. Unlike feedforward neural networks, recurrent networks with LSTM layers have loops to pass output values from one training instance to the next. LSTM layers are a powerful and widely-used tool for deep learning, and they work particularly well for time series data. By using LSTM layers, it is possible to train machine learning models that can make accurate predictions based on time series data, which can be useful for a wide range of applications, including finance, weather forecasting, and many others.</p>



<p>Also: <a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/" target="_blank" rel="noreferrer noopener">Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques</a> </p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"><div class="wp-block-image">
<figure class="aligncenter size-large is-resized"><img decoding="async" data-attachment-id="564" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-70/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/04/image-70.png" data-orig-size="431,315" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-70" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/04/image-70.png" src="https://www.relataly.com/wp-content/uploads/2020/04/image-70.png" alt="LSTM layer as part of a time series regression model, Rolling forecasting" class="wp-image-564" width="349" height="255" srcset="https://www.relataly.com/wp-content/uploads/2020/04/image-70.png 431w, https://www.relataly.com/wp-content/uploads/2020/04/image-70.png 300w" sizes="(max-width: 349px) 100vw, 349px" /></figure>
</div></div>
</div>



<h2 class="wp-block-heading">The Training Process of a Recurrent Neural Network</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>When training neural networks, the process typically involves multiple epochs, where an epoch refers to a single pass through the entire training dataset. During each epoch, the neural network receives the entire training data and adjusts its weights accordingly through forward and backward propagation. The batch size, which determines the number of examples passed through the network at once, also affects how the weights are updated between neurons.</p>



<p>It&#8217;s important to note that one epoch is usually not enough for the model to learn the underlying patterns and relationships in the data, leading to underfitting and poor performance in prediction tasks. Hence, multiple epochs are often needed to fine-tune the network and improve its predictive capabilities. However, care must be taken not to overfit the model by choosing too many epochs. Overfitting occurs when the model becomes too complex and performs well on the training data but poorly on any other data, resulting in poor generalization.</p>



<p>To avoid overfitting, we can employ various techniques such as early stopping, where the training process is stopped when the validation loss begins to increase, indicating that the model is overfitting. Another method is to use regularization techniques such as L1 or L2 regularization, dropout, or data augmentation to prevent overfitting and improve the model&#8217;s generalization capabilities.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image size-large"><img decoding="async" width="506" height="512" data-attachment-id="13202" data-permalink="https://www.relataly.com/flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-copy-2-min/" data-orig-file="https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png" data-orig-size="1004,1016" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc &amp;#8211; Copy (2)-min" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png" src="https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min-506x512.png" alt="" class="wp-image-13202" srcset="https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png 506w, https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png 296w, https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png 768w, https://www.relataly.com/wp-content/uploads/2023/03/Flo7up_a_running_robot_winning_a_marathon_colorful_pop_art_1289a27d-9bfe-4495-af3e-39640535cccc-Copy-2-min.png 1004w" sizes="(max-width: 506px) 100vw, 506px" /></figure>
</div>
</div>



<h2 class="wp-block-heading">Functioning of an LSTM layer</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>An LSTM (Long Short-Term Memory) layer is a specialized type of recurrent neural network (RNN) layer that is specifically designed for processing and making predictions based on time series data. Unlike traditional RNNs, which suffer from the vanishing gradient problem, LSTM layers can maintain a memory of past events and selectively use this memory to make predictions about future events.</p>



<p>LSTM layers accomplish this by incorporating several unique mechanisms, such as gates and cell states. The gates control the flow of information into and out of the cell, while the cell state represents the memory of the network. These mechanisms work together to enable LSTM layers to learn and make predictions based on long-term dependencies in the data, which is a characteristic of many time series datasets.</p>



<p>One of the key advantages of using LSTM layers for time series forecasting is their ability to generate predictions for multiple timesteps. This is achieved by iteratively reusing the model outputs from the previous training run as input for the next prediction step. This approach, known as a rolling forecast, allows the model to make accurate predictions for multiple time steps into the future.</p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="566" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-72/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/04/image-72.png" data-orig-size="937,229" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-72" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/04/image-72.png" src="https://www.relataly.com/wp-content/uploads/2020/04/image-72.png" alt="Functioning of an LSTM layer as it is used for time-series prediction models. Rolling forecasting" class="wp-image-566" width="751" height="184" srcset="https://www.relataly.com/wp-content/uploads/2020/04/image-72.png 937w, https://www.relataly.com/wp-content/uploads/2020/04/image-72.png 300w, https://www.relataly.com/wp-content/uploads/2020/04/image-72.png 768w" sizes="(max-width: 751px) 100vw, 751px" /><figcaption class="wp-element-caption">Functioning of an LSTM layer</figcaption></figure>



<h2 class="wp-block-heading">LSTM Components</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>To understand how an LSTM layer works, it is helpful to think about the layer as consisting of several different components, each of which has a specific role in the overall operation of the layer. These components include the following:</p>



<ol class="wp-block-list">
<li><strong>Input gate: </strong>The input gate controls which information from the input data will be passed on to the cell state. The input gate uses a sigmoid activation function to determine which information should be retained and which should be discarded.</li>



<li><strong>Forget gate: </strong>The forget gate controls which information from the cell state will be discarded. The forget gate uses a sigmoid activation function to determine which information should be forgotten and which should be retained.</li>



<li><strong>Cell state:</strong> The cell state is a vector that contains the information that is retained by the LSTM layer. This information is updated at each time step based on the input from the input gate and the forget gate.</li>



<li><strong>Output gate: </strong>The output gate controls which information from the cell state will be passed on to the output of the LSTM layer. The output gate uses a sigmoid activation function to determine which information should be retained and which should be discarded.</li>
</ol>



<p>This article uses LSTM layers combined with a rolling forecast approach to predict the course of a sinus curve with a linear slope. The result is a multi-step time series forecast. </p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h2 class="wp-block-heading" id="h-creating-a-rolling-multi-step-time-series-forecast-in-python">Creating a Rolling Multi-Step Time Series Forecast in Python</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>In this tutorial, we will explore the process of creating a rolling multi-step forecast using Python. Our dataset for this exercise will be a synthetically generated rising sine curve, which we will use to demonstrate the principles of multi-step time series forecasting.</p>



<p>By following the steps outlined in this tutorial, you will gain a comprehensive understanding of the process involved in multi-step time series forecasting. This will include techniques for data preprocessing, feature engineering, and model selection.</p>



<p>To get started, we have made the code available on our GitHub repository. You can easily access and follow along with the tutorial by downloading the code and running it on your local machine. This will enable you to experiment with different parameters and modify the code to suit your specific requirements.</p>



<div class="wp-block-kadence-advancedbtn kb-buttons-wrap kb-btns_3da302-75"><a class="kb-button kt-button button kb-btn_e56730-a6 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-tutorials/blob/master/01%20Time%20Series%20Forecasting%20%26%20Regression/005%20Multi-step%20Rolling%20Forecasting.ipynb" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fe_eye kt-btn-icon-side-left"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg></span><span class="kt-btn-inner-text">View on GitHub </span></a>

<a class="kb-button kt-button button kb-btn_e50bbe-02 kt-btn-size-standard kt-btn-width-type-full kb-btn-global-inherit  kt-btn-has-text-true kt-btn-has-svg-true  wp-block-button__link wp-block-kadence-singlebtn" href="https://github.com/flo7up/relataly-public-python-API-tutorials" target="_blank" rel="noreferrer noopener"><span class="kb-svg-icon-wrap kb-svg-icon-fa_github kt-btn-icon-side-left"><svg viewBox="0 0 496 512"  fill="currentColor" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span><span class="kt-btn-inner-text">Relataly GitHub Repo </span></a></div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"></div>
</div>



<h3 class="wp-block-heading" id="h-prerequisites">Prerequisites</h3>



<p>Before starting the coding part, make sure that you have set up your <a href="https://www.python.org/downloads/" target="_blank" rel="noreferrer noopener">Python 3</a> environment and required packages. If you don’t have an environment yet, you can follow&nbsp;<a href="https://www.relataly.com/anaconda-python-environment-machine-learning/1663/" target="_blank" rel="noreferrer noopener">this tutorial</a>&nbsp;to set up the&nbsp;<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noreferrer noopener">Anaconda environment</a>.</p>



<p>Also, make sure you install all required packages. In this tutorial, we will be working with the following standard packages:&nbsp;</p>



<ul class="wp-block-list">
<li><em><a href="https://pandas.pydata.org/" target="_blank" rel="noreferrer noopener">pandas</a></em></li>



<li><em><a href="https://numpy.org/" target="_blank" rel="noreferrer noopener">NumPy</a></em></li>



<li><a href="https://docs.python.org/3/library/math.html" target="_blank" rel="noreferrer noopener">math</a></li>



<li><em><a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a></em></li>
</ul>



<p>In addition, we will be using <em><a href="https://keras.io/" target="_blank" rel="noreferrer noopener">Keras&nbsp;</a></em>(2.0 or higher) with <a href="https://www.tensorflow.org/" target="_blank" rel="noreferrer noopener"><em>Tensorflow</em> </a>backend and the machine learning library <a href="https://scikit-learn.org/stable/" target="_blank" rel="noreferrer noopener">Scikit-learn</a>.</p>



<p>You can install packages using console commands:</p>



<ul class="wp-block-list">
<li><em>pip install &lt;package name&gt;</em></li>



<li><em>conda install &lt;package name&gt;</em>&nbsp;(if you are using the anaconda packet manager)</li>
</ul>



<h3 class="wp-block-heading" id="h-step-1-generating-synthetic-data">Step #1 Generating Synthetic Data</h3>



<p>We begin by loading the required packages and creating a synthetic dataset. The synthetic data contains 300 values of the sinus function combined with a slight linear upward slope of 0.02. The code below creates the data and visualizes it in a line plot.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># A tutorial for this file is available at www.relataly.com

import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, TimeDistributed, Dropout, Activation
from sklearn.preprocessing import RobustScaler
from sklearn.metrics import mean_absolute_error
import tensorflow as tf
import seaborn as sns
sns.set_style('white', { 'axes.spines.right': False, 'axes.spines.top': False})

# check the tensorflow version and the number of available GPUs
print('Tensorflow Version: ' + tf.__version__)
physical_devices = tf.config.list_physical_devices('GPU')
print(&quot;Num GPUs:&quot;, len(physical_devices))

# Creating the synthetic sinus curve dataset
steps = 300
gradient = 0.02
list_a = []
for i in range(0, steps, 1):
    y = round(gradient * i + math.sin(math.pi * 0.125 * i), 5)
    list_a.append(y)
df = pd.DataFrame({&quot;sine_curve&quot;: list_a}, columns=[&quot;sine_curve&quot;])

# Visualizing the data
fig, ax1 = plt.subplots(figsize=(16, 4))
ax1.xaxis.set_major_locator(plt.MaxNLocator(30))
plt.title(&quot;Sinus Data&quot;)
sns.lineplot(data=df[[&quot;sine_curve&quot;]], color=&quot;#039dfc&quot;)
plt.legend()
plt.show()</pre></div>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="8554" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-17-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/image-17.png" data-orig-size="1155,339" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-17" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/image-17.png" src="https://www.relataly.com/wp-content/uploads/2022/05/image-17-1024x301.png" alt="" class="wp-image-8554" width="816" height="240" srcset="https://www.relataly.com/wp-content/uploads/2022/05/image-17.png 1024w, https://www.relataly.com/wp-content/uploads/2022/05/image-17.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/image-17.png 768w, https://www.relataly.com/wp-content/uploads/2022/05/image-17.png 1155w" sizes="(max-width: 816px) 100vw, 816px" /></figure>



<p>The signal curve oscillates and is steadily moving upward.</p>



<h3 class="wp-block-heading">Step #2 Preprocessing</h3>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p>Next, we preprocess the data to bring it into the shape required by our neural network model. Running the code below will perform the following tasks:</p>



<ul class="wp-block-list">
<li>Scale the data to a standard range.</li>



<li>Partition the data into multiple periods with a predefined sequence length. Each partition series contains 110 data points. The number of steps needs to match the number of neurons in the first layer of the neural network architecture. </li>



<li>Split the data into two separate datasets for training and testing.</li>
</ul>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<p>Also: <a href="https://www.relataly.com/feature-engineering-for-multivariate-time-series-models-with-python/1813/" target="_blank" rel="noreferrer noopener">Mastering Multivariate Stock Market Prediction with Python: A Guide to Effective Feature Engineering Techniques</a></p>
</div>
</div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">data = df.copy()

# Get the number of rows in the data
nrows = df.shape[0]

# Convert the data to numpy values
np_data_unscaled = np.array(df)
np_data_unscaled = np.reshape(np_data_unscaled, (nrows, -1))
print(np_data_unscaled.shape)

# Transform the data by scaling each feature to a range between 0 and 1
scaler = RobustScaler()
np_data_scaled = scaler.fit_transform(np_data_unscaled)

# Set the sequence length - this is the timeframe used to make a single prediction
sequence_length = 110

# Prediction Index
index_Close = 0

# Split the training data into train and train data sets
# As a first step, we get the number of rows to train the model on 80% of the data 
train_data_len = math.ceil(np_data_scaled.shape[0] * 0.8)

# Create the training and test data
train_data = np_data_scaled[0:train_data_len, :]
test_data = np_data_scaled[train_data_len - sequence_length:, :]

# The RNN needs data with the format of [samples, time steps, features]
# Here, we create N samples, sequence_length time steps per sample, and 6 features
def partition_dataset(sequence_length, data):
    x, y = [], []
    data_len = data.shape[0]
    for i in range(sequence_length, data_len):
        x.append(data[i-sequence_length:i,:]) #contains sequence_length values 0-sequence_length * columsn
        y.append(data[i, index_Close]) #contains the prediction values for validation (3rd column = Close),  for single-step prediction
    
    # Convert the x and y to numpy arrays
    x = np.array(x)
    y = np.array(y)
    return x, y

# Generate training data and test data
x_train, y_train = partition_dataset(sequence_length, train_data)
x_test, y_test = partition_dataset(sequence_length, test_data)

# Print the shapes: the result is: (rows, training_sequence, features) (prediction value, )
print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)

# Validate that the prediction value and the input match up
# The last close price of the second input sample should equal the first prediction value
print(x_test[1][sequence_length-1][index_Close])
print(y_test[0])</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">(130, 110, 1) (130,)
(60, 110, 1) (60,)
0.599655121905751
0.599655121905751</pre></div>



<h3 class="wp-block-heading" id="h-step-3-preparing-data-for-the-multi-step-time-series-forecasting-model">Step #3 Preparing Data for the Multi-Step Time Series Forecasting Model</h3>



<p>Now that we have prepared the synthetic data, we need to determine the architecture of our neural network. There are no clear guidelines available on how to configure a neural network. Most problems are unique, and the model settings depend on the nature and extent of the data. Thus, configuring LSTM layers can be a real challenge. </p>



<h4 class="wp-block-heading" id="h-3-1-overview-of-model-parameters">3.1 Overview of Model Parameters</h4>



<p>Finding the optimal configuration is often a process of trial and error. Below you find a list of model parameters with which you can experiment:</p>



<div class="wp-block-kadence-infobox kt-info-box_298505-da"><div class="kt-blocks-info-box-link-wrap kt-blocks-info-box-media-align-top kt-info-halign-left"><div class="kt-blocks-info-box-media-container"><div class="kt-blocks-info-box-media kt-info-media-animate-none"><div class="kadence-info-box-icon-container kt-info-icon-animate-none"><div class="kadence-info-box-icon-inner-container"><span class="kb-svg-icon-wrap kb-svg-icon-fe_cpu kt-info-svg-icon"><svg viewBox="0 0 24 24"  fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"  aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2" ry="2"/><rect x="9" y="9" width="6" height="6"/><line x1="9" y1="1" x2="9" y2="4"/><line x1="15" y1="1" x2="15" y2="4"/><line x1="9" y1="20" x2="9" y2="23"/><line x1="15" y1="20" x2="15" y2="23"/><line x1="20" y1="9" x2="23" y2="9"/><line x1="20" y1="14" x2="23" y2="14"/><line x1="1" y1="9" x2="4" y2="9"/><line x1="1" y1="14" x2="4" y2="14"/></svg></span></div></div></div></div><div class="kt-infobox-textcontent"><p class="kt-blocks-info-box-text"><strong>Keras model parameters used in this tutorial</strong><br/><strong>epoch:</strong> An epoch is an iteration over the entire x_train and y_train data provided. <br/><strong>Batch_size:</strong> Number of samples per gradient update. If unspecified, batch_size is 32.<br/><strong>Activation: </strong>Mathematical equations determine whether a neuron in the network should activate (“fired”) or not. <br/><strong>Input_shape:</strong> Tensor with shape: (batch_size, &#8230;, input_dim). <br/><strong>Input_len:</strong> the length of the generated input sequence.<br/><strong>Return_sequences: </strong>If set to false, the layer will return the final output in the output sequence. If true, the layer returns the entire series.<br/><strong>Loss</strong>: The loss function tells the model how to evaluate its performance so that the weights can be updated to reduce the loss on the following evaluation.<br/><strong>Optimizer:</strong> Every time a neural network finishes processing a batch through the network and generates prediction results, the optimizer decides how to adjust weights between the nodes.<br/></p></div></div></div>



<p><em>Source: keras.io/layers/recurrent</em> &#8211; view the Keras documentation for a complete list of parameters.</p>



<h4 class="wp-block-heading" id="h-3-2-choosing-model-parameters">3.2 Choosing Model Parameters</h4>



<p>Finding a suitable architecture for a neural network is not easy and requires a systematic approach. A common practice is to start with an established standard architecture. We can then change the model parameters slightly and record the configurations and outcomes. We can also automate configuring and testing the model (<a href="https://www.relataly.com/category/machine-learning/hyperparameter-tuning/" target="_blank" rel="noreferrer noopener">Hyperparameter Tuning</a>). Still, because this is not the focus of this article, we will use a manual approach.</p>



<p>We start with five epochs, a batch_size of 1, and configure our recurrent model with one LSTM layer with 110 neurons, corresponding to a data slice of 110 input values. In addition, we add a dense layer that provides us with a single output value.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Configure the neural network model
epochs = 12; batch_size = 1;

# Model with n_neurons = inputshape Timestamps, each with x_train.shape[2] variables
n_neurons = x_train.shape[1] * x_train.shape[2]
model = Sequential()
model.add(LSTM(n_neurons, return_sequences=True, input_shape=(x_train.shape[1], 1)))
model.add(LSTM(n_neurons, return_sequences=False))
model.add(Dense(5))
model.add(Dense(1))
model.compile(optimizer=&quot;adam&quot;, loss=&quot;mean_squared_error&quot;)</pre></div>



<h3 class="wp-block-heading" id="h-step-3-training-the-prediction-model">Step #3 Training the Prediction Model</h3>



<p>Now that we have defined the architecture of our recurrent neural network, the next step is to train the model. Training the model involves using a training dataset to adjust the weights and biases of the network so that it can make accurate predictions on new data. This process is done using an optimization algorithm, such as gradient descent, to minimize the difference between the predicted values and the true values. </p>



<p>Training can be time-consuming, especially for large datasets or complex models, and the amount of time it takes may vary depending on the performance of your computer. The complexity of our model is relatively low, and we only use five training epochs. It should thus only take a couple of minutes to train the model.</p>



<p>It is important to monitor the training process to ensure that the model is learning effectively and to identify any potential issues or problems. Once the training is complete, we can test the model on a separate test dataset to evaluate its performance.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Train the model
history = model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs)</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Epoch 1/12
130/130 [==============================] - 7s 28ms/step - loss: 0.0544
Epoch 2/12
130/130 [==============================] - 4s 27ms/step - loss: 0.0041
Epoch 3/12
130/130 [==============================] - 4s 28ms/step - loss: 3.5105e-04
Epoch 4/12
130/130 [==============================] - 4s 28ms/step - loss: 4.3903e-05
Epoch 5/12
130/130 [==============================] - 4s 28ms/step - loss: 5.9422e-05
Epoch 6/12
130/130 [==============================] - 4s 28ms/step - loss: 5.7988e-05
Epoch 7/12
130/130 [==============================] - 4s 28ms/step - loss: 8.3814e-04
Epoch 8/12
130/130 [==============================] - 4s 28ms/step - loss: 1.9122e-04
Epoch 9/12
130/130 [==============================] - 4s 28ms/step - loss: 0.0014
Epoch 10/12
130/130 [==============================] - 4s 29ms/step - loss: 0.0114
Epoch 11/12
130/130 [==============================] - 4s 28ms/step - loss: 0.0013
Epoch 12/12
130/130 [==============================] - 4s 28ms/step - loss: 3.4571e-05</pre></div>



<h3 class="wp-block-heading" id="h-step-4-predicting-a-single-step-ahead">Step #4 Predicting a Single-step Ahead</h3>



<p>We continue by making single-step predictions based on the training data. Then we calculate the mean squared error and the median error to measure the performance of our model.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Reshape the data, so that we get an array with multiple test datasets
x_test_np = np.array(x_test)
x_test_reshape = np.reshape(x_test_np, (x_test_np.shape[0], x_test_np.shape[1], 1))

# Get the predicted values
y_pred = model.predict(x_test_reshape)
y_pred_unscaled = scaler.inverse_transform(y_pred)
y_test_unscaled = scaler.inverse_transform(y_test.reshape(-1, 1))

# Mean Absolute Error (MAE)
MAE = mean_absolute_error(y_test_unscaled, y_pred_unscaled)
print(f'Median Absolute Error (MAE): {np.round(MAE, 2)}')

# Mean Absolute Percentage Error (MAPE)
MAPE = np.mean((np.abs(np.subtract(y_test_unscaled, y_pred_unscaled)/ y_test_unscaled))) * 100
print(f'Mean Absolute Percentage Error (MAPE): {np.round(MAPE, 2)} %')

# Median Absolute Percentage Error (MDAPE)
MDAPE = np.median((np.abs(np.subtract(y_test_unscaled, y_pred_unscaled)/ y_test_unscaled)) ) * 100
print(f'Median Absolute Percentage Error (MDAPE): {np.round(MDAPE, 2)} %')</pre></div>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:false,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;null&quot;,&quot;mime&quot;:&quot;text/plain&quot;,&quot;theme&quot;:&quot;3024-day&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Plain Text&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;text&quot;}">Out: me: 0.0223, rmse: 0.0206</pre></div>



<p>Both the mean error and the squared mean error are pretty small. As a result, the values predicted by our model are close to the actual values of the ground truth. Even though it is unlikely because of the small number of epochs, it could still be that the model is over-fitting.</p>



<h3 class="wp-block-heading" id="h-step-5-visualizing-predictions-and-loss">Step #5 Visualizing Predictions and Loss</h3>



<p>Next, we look at the quality of the training predictions by plotting the training predictions and the actual values (i.e., ground truth).</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Visualize the data
#train = df[:train_data_len]
df_valid_pred = df[train_data_len:]
df_valid_pred.insert(1, &quot;y_pred&quot;, y_pred_unscaled, True)

# Create the lineplot
fig, ax1 = plt.subplots(figsize=(32, 5), sharex=True)
ax1.tick_params(axis=&quot;x&quot;, rotation=0, labelsize=10, length=0)
plt.title(&quot;Predictions vs Ground Truth&quot;)
sns.lineplot(data=df_valid_pred)
plt.show()</pre></div>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="176" data-attachment-id="11772" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-20-3/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/12/image-20.png" data-orig-size="1820,312" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-20" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/12/image-20.png" src="https://www.relataly.com/wp-content/uploads/2022/12/image-20-1024x176.png" alt="multi step time series regression rollng forecasting approach training predictions vs ground truth in python with neural networks" class="wp-image-11772" srcset="https://www.relataly.com/wp-content/uploads/2022/12/image-20.png 1024w, https://www.relataly.com/wp-content/uploads/2022/12/image-20.png 300w, https://www.relataly.com/wp-content/uploads/2022/12/image-20.png 768w, https://www.relataly.com/wp-content/uploads/2022/12/image-20.png 1536w, https://www.relataly.com/wp-content/uploads/2022/12/image-20.png 1820w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The smaller the area between the two lines, the better our model predictions. So we can tell from the plot that the predictions are not entirely wrong. We also check the learning path of the regression model. </p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Plot training &amp; validation loss values
fig, ax = plt.subplots(figsize=(10, 5), sharex=True)
sns.lineplot(data=history.history[&quot;loss&quot;])
plt.title(&quot;Model loss&quot;)
plt.ylabel(&quot;Loss&quot;)
plt.xlabel(&quot;Epoch&quot;)
ax.xaxis.set_major_locator(plt.MaxNLocator(epochs))
plt.legend([&quot;Train&quot;], loc=&quot;upper left&quot;)
plt.grid()
plt.show()</pre></div>



<figure class="wp-block-image size-large"><img decoding="async" width="349" height="333" data-attachment-id="572" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-75/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/04/image-75.png" data-orig-size="349,333" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-75" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/04/image-75.png" src="https://www.relataly.com/wp-content/uploads/2020/04/image-75.png" alt="loss function of our neural network model - multi-step time series regression" class="wp-image-572" srcset="https://www.relataly.com/wp-content/uploads/2020/04/image-75.png 349w, https://www.relataly.com/wp-content/uploads/2020/04/image-75.png 300w" sizes="(max-width: 349px) 100vw, 349px" /></figure>



<p>The loss drops quickly, and after five epochs, the model seems to have converged.</p>



<h3 class="wp-block-heading" id="h-step-6-rolling-forecasting-creating-a-multi-step-time-series-forecast">Step #6 Rolling Forecasting: Creating a Multi-step Time Series Forecast</h3>



<p>Next, we will generate the rolling multi-step forecast. This approach is different from a single-step approach in that we predict several points of a signal within a prediction window and not just a single value. However, the prediction quality decreases over more extended periods because reusing predictions creates a feedback loop that amplifies potential errors over time. </p>



<p>Also: <a href="https://www.relataly.com/regression-error-metrics-python/923/" target="_blank" rel="noreferrer noopener">Measuring Regression Errors with Python</a> </p>



<p>The forecasting process begins with an initial prediction for a single time step. After that, we add the predicted value to the input values for another projection, and so on. In this way, we create the rolling forecast with multiple time steps.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}"># Settings and Model Labels
rolling_forecast_range = 30
titletext = &quot;Forecast Chart Model A&quot;
ms = [
    [&quot;epochs&quot;, epochs],
    [&quot;batch_size&quot;, batch_size],
    [&quot;lstm_neuron_number&quot;, n_neurons],
    [&quot;rolling_forecast_range&quot;, rolling_forecast_range],
    [&quot;layers&quot;, &quot;LSTM, DENSE(1)&quot;],
]
settings_text = &quot;&quot;
lms = len(ms)
for i in range(0, lms):
    settings_text += ms[i][0] + &quot;: &quot; + str(ms[i][1])
    
    if i &lt; lms - 1:
        settings_text = settings_text + &quot;,  &quot;

# Making a Multi-Step Prediction
# Create the initial input data
new_df = df.filter([&quot;sine_curve&quot;])
for i in range(0, rolling_forecast_range):
    # Select the last sequence from the dataframe as input for the prediction model
    last_values = new_df[-n_neurons:].values
    
    # Scale the input data and bring it into shape
    last_values_scaled = scaler.transform(last_values)
    X_input = np.array(last_values_scaled).reshape([1, 110, 1])
    X_test = np.reshape(X_input, (X_input.shape[0], X_input.shape[1], 1))
    
    # Predict and unscale the predictions
    pred_value = model.predict(X_input)
    pred_value_unscaled = scaler.inverse_transform(pred_value)
    
    # Add the prediction to the next input dataframe
    new_df = pd.concat([new_df, pd.DataFrame({&quot;sine_curve&quot;: pred_value_unscaled[0, 0]}, index=new_df.iloc[[-1]].index.values + 1)])
    new_df_length = new_df.size
forecast = new_df[new_df_length - rolling_forecast_range : new_df_length].rename(
    columns={&quot;sine_curve&quot;: &quot;Forecast&quot;}
)</pre></div>



<p>We can plot the forecast together with the ground truth.</p>



<div class="wp-block-codemirror-blocks-code-block code-block"><pre class="CodeMirror" data-setting="{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;python&quot;,&quot;mime&quot;:&quot;text/x-python&quot;,&quot;theme&quot;:&quot;monokai&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:true,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;Python&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;python&quot;}">#Visualize the results
validxs = df_valid_pred.copy()
dflen = new_df.size - 1
dfs = pd.concat([validxs, forecast], sort=False)
dfs.at[dflen, &quot;y_pred_train&quot;] = dfs.at[dflen, &quot;y_pred&quot;]
dfs

# Zoom in to a closer timeframe
df_zoom = dfs[dfs.index &gt; 200]

# Visualize the data
fig, ax = plt.subplots(figsize=(16, 5), sharex=True)
ax.tick_params(axis=&quot;x&quot;, rotation=0, labelsize=10, length=0)
ax.xaxis.set_major_locator(plt.MaxNLocator(rolling_forecast_range))
plt.title('Forecast And Predictions (y_pred)')
sns.lineplot(data=df_zoom[[&quot;sine_curve&quot;, &quot;y_pred&quot;]], ax=ax)
sns.scatterplot(data=df_zoom[[&quot;Forecast&quot;]], x=df_zoom.index, y='Forecast', linewidth=1.0, ax=ax)
plt.legend([&quot;x_train&quot;, &quot;y_pred&quot;, &quot;y_test_rolling&quot;], loc=&quot;lower right&quot;)
ax.annotate('ModelSettings: ' + settings_text, xy=(0.06, .015),  xycoords='figure fraction', horizontalalignment='left', verticalalignment='bottom', fontsize=10)
plt.show()</pre></div>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="8549" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-16/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2022/05/image-16.png" data-orig-size="1185,425" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-16" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2022/05/image-16.png" src="https://www.relataly.com/wp-content/uploads/2022/05/image-16-1024x367.png" alt="Forecast for the sinus curve with 30 timesteps (initial try)" class="wp-image-8549" width="766" height="274" srcset="https://www.relataly.com/wp-content/uploads/2022/05/image-16.png 1024w, https://www.relataly.com/wp-content/uploads/2022/05/image-16.png 300w, https://www.relataly.com/wp-content/uploads/2022/05/image-16.png 768w, https://www.relataly.com/wp-content/uploads/2022/05/image-16.png 1185w" sizes="(max-width: 766px) 100vw, 766px" /></figure>



<p>The model learned to predict the periodic movement of the curve and thus succeeds in modeling its further course. However, the amplitude of the predicted curve increases over time, which increases the prediction errors.</p>



<h3 class="wp-block-heading" id="h-step-7-comparing-results-for-different-parameters">Step #7 Comparing Results for Different Parameters</h3>



<p>There is plenty of room to improve the forecasting model further. So far, our model seems not to consider that the amplitude of the sinus curve is gradually increasing. Over time, errors are amplified, leading to a growing deviation from the ground truth signal.</p>



<p>We can improve the model by changing the model parameters and the model architecture. However, I would not recommend changing them one at a time.</p>



<p>I tested several model configurations with varying epochs and neuron numbers/sample sizes to further optimize the model. Parameters such as Batch_size (=1), the timeframe for the forecast (=30 timesteps), and the model architecture (=1 LSTM Layer, 1 DENSE Layer) were left unchanged. The illustrations below show the results:</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="1379" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-48-2/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/05/image-48.png" data-orig-size="823,542" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-48" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/05/image-48.png" src="https://www.relataly.com/wp-content/uploads/2020/05/image-48.png" alt="Different configurations of the time-series forecasting model" class="wp-image-1379" width="1108" height="730" srcset="https://www.relataly.com/wp-content/uploads/2020/05/image-48.png 823w, https://www.relataly.com/wp-content/uploads/2020/05/image-48.png 300w, https://www.relataly.com/wp-content/uploads/2020/05/image-48.png 768w" sizes="(max-width: 1108px) 100vw, 1108px" /><figcaption class="wp-element-caption">Different designs of the rolling time series forecasting model</figcaption></figure>



<p>The model that looks most promising is model #6. This model considers both the periodic movements of the sinus curve and the steadily increasing slope. The configuration of this model uses 15 epochs and 115 neurons/sample values. </p>



<p>Errors are amplified over more extended periods when they enter a feedback loop. For this reason, we can see in the graph below that the prediction accuracy decreases over time.</p>



<figure class="wp-block-image size-large is-resized"><img decoding="async" data-attachment-id="614" data-permalink="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/image-88/#main" data-orig-file="https://www.relataly.com/wp-content/uploads/2020/04/image-88.png" data-orig-size="930,299" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="image-88" data-image-description="" data-image-caption="" data-large-file="https://www.relataly.com/wp-content/uploads/2020/04/image-88.png" src="https://www.relataly.com/wp-content/uploads/2020/04/image-88.png" alt="Long-time multi-step forecast" class="wp-image-614" width="1070" height="344" srcset="https://www.relataly.com/wp-content/uploads/2020/04/image-88.png 930w, https://www.relataly.com/wp-content/uploads/2020/04/image-88.png 300w, https://www.relataly.com/wp-content/uploads/2020/04/image-88.png 768w" sizes="(max-width: 1070px) 100vw, 1070px" /><figcaption class="wp-element-caption">Long-time multi-step forecast (model #6)</figcaption></figure>



<h2 class="wp-block-heading" id="h-summary">Summary</h2>



<p>In this tutorial, we have created a rolling time-series forecast for a rising sine curve. A multi-step forecast helps better understand how a signal will develop over a more extended period. Finally, we have tested and compared different model variants and selected the best-performing model. </p>



<p>There are several ways to improve model accuracy further. For example, the current network architecture was kept simple and only had a single LSTM layer. Consequently, we could try to add additional layers. Another possibility is to experiment with different hyperparameters. For example, we could increase the training epochs and use dropout to prevent overfitting. In addition, we could experiment with other activation functions. Feel free to try it out.</p>



<p>I hope this article was helpful. If you have questions remaining, let me know in the comments.</p>



<p>Another forecasting approach is to train a neural network model that predicts multiple outputs per input batch. Another relataly article demonstrates how this multi-output regression approach works: <a href="https://www.relataly.com/stock-market-prediction-multi-step-regression-using-neural-networks-with-multiple-outputs-in-python/5800/" target="_blank" rel="noreferrer noopener">Stock Market Prediction &#8211; Multi-output regression in Python</a>.</p>



<p>Also, consider non-neural network approaches to time series forecasting, such as <a href="https://www.relataly.com/forecasting-beer-sales-with-arima-in-python/2884/" target="_blank" rel="noreferrer noopener">ARIMA</a>, which can achieve great results too.</p>



<h2 class="wp-block-heading">Sources and Further Reading</h2>



<div style="display: inline-block;">
  <iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=3030181162&amp;asins=3030181162&amp;linkId=669e46025028259138fbb5ccec12dfbe&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1999579577&amp;asins=1999579577&amp;linkId=91d862698bf9010ff4c09539e4c49bf4&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1839217715&amp;asins=1839217715&amp;linkId=356ba074068849ff54393f527190825d&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
<iframe sandbox="allow-popups allow-scripts allow-modals allow-forms allow-same-origin" style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//ws-eu.amazon-adsystem.com/widgets/q?ServiceVersion=20070822&amp;OneJS=1&amp;Operation=GetAdHtml&amp;MarketPlace=DE&amp;source=ss&amp;ref=as_ss_li_til&amp;ad_type=product_link&amp;tracking_id=flo7up-21&amp;language=de_DE&amp;marketplace=amazon&amp;region=DE&amp;placement=1492032646&amp;asins=1492032646&amp;linkId=2214804dd039e7103577abd08722abac&amp;show_border=true&amp;link_opens_in_new_window=true"></iframe>
</div>



<p class="has-contrast-2-color has-base-3-background-color has-text-color has-background"><em>The links above to Amazon are affiliate links. By buying through these links, you support the Relataly.com blog and help to cover the hosting costs. Using the links does not affect the price.</em></p>
<p>The post <a href="https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/">Rolling Time Series Forecasting: Creating a Multi-Step Prediction for a Rising Sine Curve using Neural Networks in Python</a> appeared first on <a href="https://www.relataly.com">relataly.com</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.relataly.com/multi-step-time-series-forecasting-a-step-by-step-guide/275/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">275</post-id>	</item>
	</channel>
</rss>
