预测控制_预测

在本文中,我们概述了使PCC系统超越当前时间范围的预测框架。 我们描述了时间序列预测的基本概念,并检查了整个系统中使用的数据。 我们检查了处理上一年度体育比赛的静态数据和将日志流式传输到聚合算法中的运动数据的体系结构。 接下来,我们讨论我们自己的定制预测器的开发,包括标准回归,向量分析和服务器日志数据的数值分析。 接下来的部分描述了我们开发的几种后处理器的开发,这些后处理器可以平滑并消除异常峰值。 最后,我们展示了预测系统的总体准确性及其结果。 在本文中,我们包括许多示例代码清单,作为开发人员的示例。

在整个比赛日程中,在典型的比赛日中,我们在三活动云中节省了多达51%的计算时间或134.5个小时的计算时间。 以这种速度,在107个比赛日中,PCC最多可以节省14388小时的计算资源。

时间序列预测基础

时间序列是预测未来数据趋势的基本技术。 最有效的预测类型通常是通过对具有基于循环模式的数据进行回归来实现的。 自回归综合移动平均值(ARIMA)是一种在整个预测领域中普遍使用的模型。 在我们的工作中,我们开发了实现高级数值分析的其他算法,以补充ARIMA等传统技术。 例如,下面的方程式显示了一个传统的循环模式,类似于我们过滤掉尖峰事件时的网络流量数据:

该方程式编码了重要的信息趋势,可帮助我们预测未来的服务器使用情况。 的

变量代表信号的幅度,它决定了我们必须维护的整个服务器中的最大点击次数。 的
变量编码趋势或数据通常变化的方式。 在我们的问题空间中,我们可以确定流量或坡度的增长。 变量
让我们知道提供锦标赛内容的服务器的稳定点击率。 最后,三角函数
提供我们数据的季节性。 定制预测器的实施可以预测当前时间以外的幅度,水平,趋势和季节性,从而使PCC能够主动。

在PCC中实现自定义预测器之前,服务微服务和数字上下文用户体验仅是被动的。 图1显示,服务器容量和服务器负载之间的差异没有得到优化,并且如果方程式中的任何变量发生更改,都可能无法配置足够的服务器。 PCC制作的一种主动模型可以预测未来24小时,并缩小了服务器容量和服务器负载之间的差距。 总体而言,系统实现了10.25%的平均绝对百分比误差。

图1. PCC的总体目标是主动提供或取消提供云服务。

在整个网球大满贯赛事,大师赛和美国高尔夫赛事中,都有许多不同类型的数据可用来实现较高的预测准确性,从而持续为全球观众服务。 分数,时间表,伤害,天气,日志,推文和模拟数据为发现信号提供了基础,以进行准确的预测和预测。

图2给出了预测周期性模式并与预测模型结合以包括流量峰值的总体架构。 在步骤1,PCC预处理器将实时日志流式传输。 日志是由Python程序在所有原始计算机上累积的,并被推送到RabbitMQ。 InfoSphere Streams流程使用日志并汇总每分钟的流量计数,并将结果发送到Java应用程序。 预处理器会估算所有丢失的数据,消除可能是错误的异常事件,并使用窗口平均值对曲线进行平滑处理。 预处理后的数据被发送到时间序列集合以进行周期性预测。 根据时间范围的准确性,总共应用了五个补充预报器并将其组合在一起。 周期性预测的结果在步骤2中发送到后处理器。后处理器对给定时区的数据进行时移,对预测进行平滑处理,并消除预测流量中的任何峰值。 不断地,诸如乐谱,时间表,推文和Web内容之类的数据将流式传输到在UIMA-AS上运行的分布式特征提取器,该技术与Watson赢得Jeopardy的技术相同!

图2.体育赛事中的预测和各种数据消耗产生了高度准确的预测。

完成周期性预测后,PCC会逐步模拟高尔夫和网球比赛。 步骤4、5和6将模拟分布在多个计算节点上,以提供接近实时的模拟。 一组特征提取器可发现23个高尔夫预测器和18个网球预测器,以估计服务器流量的高峰。 例如,假设在高尔夫比赛中,PCC确定是否有一组特色高尔夫选手参加比赛。 如果是这样,流量很可能会超过高尔夫云服务。

接下来,在步骤8中,将预测模型的输出和周期性预测传递到残差预测器,以校正任何数学误差。 在步骤9,将未来需求的两个值与半衰期函数组合在一起。 半衰期功能在上一年的锦标赛上进行了训练,以将整个周期性和峰值预测强制为所需状态,以进一步提高准确性。 最后,PCC准备或取消供应云资源,以准备使用云。

记录数据

在每个事件中,我们都使用内容数据网络(CDN)缓存静态内容以保护原始服务器。 边缘请求的数量每24小时达到19亿。 当动态内容或生存时间(TTL)到期时,所有流量中约有6%滴流到源头。 在向原点发出的1.128亿个请求中,点击量的大小并非随时间平均分配。

图3.在一个典型的锦标赛中,整整24小时的边击次数为19亿个。 约5.6%的流量流向原始服务器。

如图4所示,来自原始源的实时日志通过RabbitMQ流到InfoSphere Streams,并被推送到WebSphere Java Application。 日志被解析为每分钟计数并保存到DB2中。 结果, 图2中描述的预测者可以利用最新信息来预测对未来的预测。

图4.原始服务器日志通过RabbitMQ流传输到InfoSphere Streams,最后发送到WebSphere Java应用程序。

数据预处理

用于预测的日志数据可能会出现许多来自漫游器的不规则峰值或日期和时间取整过程中丢失的数据。 这些情况中的任何一种都可能导致周期性模式在24小时内的准确性降低。 由于预测集合使用预测模型来处理流量高峰,因此平滑的总曲线可以对锦标赛内容需求进行总体估算。

清单1描述了使用移动平均窗口平滑历史日志信息的代码。 可以将窗口的持续时间参数化,以便延长时间可以进一步抑制一般的流量高峰。 类似的逻辑用于在一天的1440分钟内一分钟内估算任何丢失的流量大小。 如果缺少数据点,则平均窗口会计算一个最近邻值,以插入到时间序列数据中。

接下来,系统确定数据的季节性。 由于每天的流量趋势具有相似的模式,因此季节性被强制为24小时。 确定24小时周期的开始和结束时间后,我们将以相似的模式对历史数据进行分层。 结果,我们可以确定在以后的预报器中使用的平均曲线或周期性模式。 但是,我们也可以过滤掉任何偏离当前所有数据趋势的历史曲线。 此步骤对于删除可能缺少数据或错误日志信息的历史数据非常重要。

JSON文件中所有指定的预处理器完成后,流量趋势将发送到一组预测器。

清单1.描述Java中的移动平均处理器。
public class MovingAverageProcessor  {
	private static final double DEFAULT_LENGTH = 20;
	private final double length;

	public MovingAveragePostprocessor(TrainingFold fold, Parameters forecasterParameters, Parameters systemParameters) {
		this.length = forecasterParameters.getOrDefault("length", DEFAULT_LENGTH).doubleValue();
	}

	@Override
	public Iterable<ForecastInstant> postprocess(final Iterable<ForecastInstant> input) {
		final ImmutableList<ForecastInstant> forecast = ImmutableList.copyOf(input);
		return Iterables.transform(forecast, new Function<ForecastInstant, ForecastInstant>() {
			@Override
			public ForecastInstant apply(ForecastInstant input) {
				int index = forecast.indexOf(input);
				int start = (int) Math.max(0, index - length / 2);
				int end = (int) Math.min(forecast.size(), index + length / 2);
				return input.setValue(ForecastReportInstants.averageValue(forecast.subList(start, end)));
			}
		});
	}
}

集合历史预报员

几种不同类型的历史预测器可以相互补充,以提供提供高度准确的周期性预测的多种技术。 预报员团队实施不同的时间范围,例如短期(1-2小时),中期(10-15小时)和长期(24小时),以便在与新颖的半衰期结合使用之前进行独立评估加权方案。 图5显示了整个预测框架内的历史预测者。

图5.总体预测架构包括历史预测者的队列或集合。

在清单2中, CyclicalForecaster类为每个实现的历史预测器维护一个构建器。 通常,PCC在整个高尔夫和网球比赛中使用局部平均,调整平均,二次方,三次方和矢量预测器。 调整后的平均和部分平均预报员通过预处理阶段发现的训练数据的统计和移位平均值来提供长期预报。 平均预报员提供的基线预报可以与更精确的中期和短期预报员组合。 向量预测器是一种数值分析投影,它使用Runge-Kutta四阶(RK4)算法在当前时间范围之前的五个小时内投影到未来。 接下来,二次多项式和三次多项式预测器将回归过去,以预测未来两个小时。 该预测器为基线周期性预测增加了额外的精度。 所有预测器的组合的输出是Web服务器流量的预测周期性趋势。

清单2.循环预报器是在Java代码中实现的。
public class CyclicalForecaster {
	private static final Logger logger = LoggerFactory.getLogger(CyclicalForecaster.class);
	private final TrainingFold fold;
	private final TournamentConfiguration tourneyConfig;
	private final ForecastingConfiguration forecastingConfig;

	private CyclicalForecaster(CyclicalForecasterBuilder builder) {
		this.fold = builder.fold;
		this.forecastingConfig = builder.forecastingConfig;
		this.tourneyConfig = builder.tourneyConfig;
	}

	public Iterable<ForecastInstant> createForecast() {
		HistoricalEnsembleForecast historicalForecast = new HistoricalEnsembleForecast(this.tourneyConfig, forecastingConfig.getCompositeForecast().getCyclicalForecast());
		final Iterable<ForecastInstant> historical = historicalForecast.forecast(fold);

		logger.trace("Before Residual Forecast: {}", historical);
		final Iterable<ForecastInstant> residualForecast = doResidualForecast(historical, this.tourneyConfig);

		logger.trace("After Residual Forecast: {}", residualForecast);
		Iterable<ForecastInstant> finalOutput = residualForecast;
		for (final Postprocessor p : getPostprocessors(fold)) {
			finalOutput = p.postprocess(finalOutput);
		}
		return finalOutput;
	}

	private List<Postprocessor> getPostprocessors(final TrainingFold fold) {
		final int zoneOffset = this.tourneyConfig.getSite().getTimezoneOffset();
		return Lists.transform(this.forecastingConfig.getCompositeForecast().getCyclicalForecast().getPostprocessors(), new Function<PostprocessorValue, Postprocessor>() {
			@Override
			public Postprocessor apply(PostprocessorValue input) {
				Map<String, Number> delegate = Maps.newHashMap();
				delegate.put(Postprocessor.TIMEZONE_FOCUS, zoneOffset);
				Parameters systemParams = new Parameters(delegate);
				return input.getType().build(fold, input.getParameters(), systemParams);
			}
		});
	}
}

每个预测器都用一个生成器实例化。 从软件工程的角度来看,我们可以插入新的预测器,因为它们的开发速度非常快。 清单3描述了周期性预测器的构建器代码。

清单3.每个预测器都由一个构建器实例化。
public static class CyclicalForecasterBuilder {
		private final TournamentConfiguration tourneyConfig;
		private final ForecastingConfiguration forecastingConfig;
		private final TrainingFold fold;

		public CyclicalForecasterBuilder(TournamentConfiguration tourneyConfig, ForecastingConfiguration forecastingConfig, TrainingFold fold) {
			this.tourneyConfig = tourneyConfig;
			this.forecastingConfig = forecastingConfig;
			this.fold = fold;
		}

		public CyclicalForecaster build() {
			return new CyclicalForecaster(this);
		}
	}

偏平均预报器

部分平均预报员预测未来几个小时。 为了预测未来趋势,预测员会从当前或过去事件的每一天的历史或培训数据的最后两个小时中学习。 预报员创建一个预报曲线,该曲线是基于两个小时的匹配班次的所有班次训练曲线点的平均值。 每个点都使用当前曲线进行时间归一化,因此平均曲线的一半与当前趋势重叠。 与曲线不重叠的部分成为预测。 此外,如果时间预测在周末之内,则仅将周末趋势用作训练数据。 同样的逻辑适用于工作日,如果当前时间在一周中,则将训练数据分层以仅使用工作日趋势。 图6显示了使用三个小时的历史数据的预报器的图形表示。

图6.部分平均预报员使用任意数量的时期或季节来生成预报。

调整后的平均预测者

调整后的平均和部分平均预报器相似,但有以下两个例外:

  • 错误功能。
  • 最佳曲线匹配可在当前数据和整个先前期间之间移动每个训练曲线。

如果预测不与当前趋势重叠,则整个平均曲线即为预测。 这种情况通常发生在季节性趋势的开始。

下面的等式说明了PCC如何找到最佳曲线匹配点的最佳换挡点。

要点(

)代表定义的最佳点的编码,其中
是收益,
是时移,并且
是使用Powell Optimization在当前数据上计算最佳曲线匹配的幅度偏移。 定义了鲍威尔最优化的总体目标函数,从而使惩罚总和最小化。 结果,当前的预测会随时间推移
,服务器需求
并获得
。 Java中的实现细节可以在清单4和清单5中找到。
清单4.用Java编写的一般调整后的平均预测器。
public class AdjustedAverageForecaster extends AverageForecaster implements Forecaster {
	public static final Duration FORECAST_HORIZON = Duration.standardDays(1);
	public AdjustedAverageForecaster(final Duration forecastHorizon, final Iterable<Iterable<ForecastInstant>> trainingForecasts, final double numStandardDev) {
		super(forecastHorizon, trainingForecasts, numStandardDev);
	}

	public AdjustedAverageForecaster(final Duration forecastHorizon, final Iterable<Iterable<ForecastInstant>> trainingForecasts) {
		super(forecastHorizon, trainingForecasts);
	}

	@Override
	protected Match<Iterable<ForecastInstant>> getCurveMatch(final Iterable<ForecastInstant> curve, CurveMatcher curveMatcher) {
		return curveMatcher.getBestMatch(curve);
	}
}
清单5.普通预测员的业务发现了未来的趋势。
public abstract class AverageForecaster implements Forecaster {
	private static final double DEFAULT_NUMBER_OF_STANDARD_DEVIATIONS = 1.0;
	public static final Duration FORECAST_HORIZON = Duration.standardDays(1);
	private static final Logger logger = LoggerFactory.getLogger(AverageForecaster.class);
	private final Optional<Iterable<ForecastInstant>> weekendForecast;
	private final Iterable<ForecastInstant> forecast;
	private final Duration forecastHorizon;

	public AverageForecaster(final Duration forecastHorizon, final Iterable<Iterable<ForecastInstant>> trainingForecasts, final double numStandardDev) {
		logger.debug("Building Average Forecaster from training data.");
		forecast = ForecastReportInstants.shiftedAveragePlusStDev(trainingForecasts, numStandardDev);
		final Iterable<Iterable<ForecastInstant>> weekend = GroupingCode.filter(trainingForecasts, GroupingCode.WEEKEND);
		if (!Iterables.isEmpty(weekend)) {
			weekendForecast = Optional.of(ForecastReportInstants.shiftedAveragePlusStDev(weekend, numStandardDev));
		} else {
			weekendForecast = Optional.absent();
		}
		this.forecastHorizon = forecastHorizon;
	}
	public AverageForecaster(final Duration forecastHorizon, final Iterable<Iterable<ForecastInstant>> trainingForecasts) {
		this(forecastHorizon, trainingForecasts, DEFAULT_NUMBER_OF_STANDARD_DEVIATIONS);
	}
	@Override
	public Duration getForecastHorizon() {
		return forecastHorizon;
	}
	@Override
	public Iterable<ForecastInstant> forecast(final Iterable<ForecastInstant> curve) {
		Preconditions.checkArgument(!Iterables.isEmpty(curve));

		Iterable<ForecastInstant> average = forecast;
		if (GroupingCode.forTime(curve).contains(GroupingCode.WEEKEND) && weekendForecast.isPresent()) {
			average = weekendForecast.get();
		}
		final CurveMatcher curveMatcher = new CurveMatcher(average);
		final Match<Iterable<ForecastInstant>> match = getCurveMatch(curve, curveMatcher);
		final Iterable<ForecastInstant> unalign = addOffsetInstance(average,
			Duration.millis(curve.iterator().next().getInstantAsMillisSinceEpoch() + match.getDeltaTimeForMatch().getMillis()));
		final Iterable<ForecastInstant> adjusted = addOffsetInstance(unalign, match.getDeltaTimeForMatch().multipliedBy(-1));
		final Iterable<ForecastInstant> multiplied = multiplyValue(adjusted, match.getDeltaMagnitudeForMatch());
		return offsetValues(multiplied, -1 * match.getDeltaDemandForMatch());
	}
	protected abstract Match<Iterable<ForecastInstant>> getCurveMatch(final Iterable<ForecastInstant> curve, CurveMatcher curveMatcher);

}

矢量预测器

矢量预测器使用以下等式中所示的RK4方法,使用对数训练数据中的建模矢量图来预测未来晚上5小时内的服务器需求。 预测接受一组对齐的曲线,矢量场密度和预测长度以生成预测。 将黄土插值器应用于所有训练曲线以产生矢量场。 RK4方法应用于产生最终预测的向量字段。

清单6描述了实现等式的代码。

清单6.使用矢量预测实现的RK4 Java代码。
private Iterable<ForecastInstant> traceRK4(final Vector position, final Vector velocity, final double t, final DateTime start) {
		final FirstOrderDifferentialEquations equations = new FirstOrderDifferentialEquations() {
			@Override
			public int getDimension() {
				return 4;
			}

			@Override
			public void computeDerivatives(final double t, final double[] y, final double[] yDot) {
				Vector secondDerive = Vector.ZERO;
				try {
					secondDerive = trainedField.getComputedVector(trainedField.getXCoord(y[0]), trainedField.getYCoord(y[2]));
				} catch (final IndexOutOfBoundsException e) {
					logger.trace("Error computing derivative", e);
				}
				yDot[0] = 1;// x1'
				yDot[1] = secondDerive.getX();// x2'
				yDot[2] = y[3];// y1'
				yDot[3] = secondDerive.getY();// y2'
			}
		};
		final double[] positionAndVelocity = new double[] { position.getX(), velocity.getX(), position.getY(), velocity.getY() };

		final RungeKuttaIntegrator rk = new ClassicalRungeKuttaIntegrator(size);
		final List<ForecastInstant> data = Lists.newArrayList();
		rk.addStepHandler(new StepHandler() {
			@Override
			public void init(final double t0, final double[] y0, final double t) {
				// empty
			}

			@Override
			public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
				final double[] d = interpolator.getInterpolatedState();
				data.add(new ForecastInstant(start.plusMillis((int) d[0]), d[2]));
			}
		});
		rk.integrate(equations, 0, positionAndVelocity, t, positionAndVelocity);
		Collections.sort(data);
		return data;
	}

图7显示了来自三个数据周期的对齐曲线的示例。 对齐的曲线已通过Powell优化过程移动。 对齐曲线上的点产生矢量场,从而RK4方法创建预测。

图7.该图显示了矢量预测器的内部工作原理。

二次和三次预报器

任何类型的多项式都可以拟合或回归到历史数据上。 但是,PCC使用二次和三次形式,因为任何其他更高阶的多项式都将拟合数据。 两位预报员都将过去的任何参数化时间段回归到历史数据上。 结果模型包括一个二次方程(

)和三次模型(
)。

未来时间的输入

进入模型可得出服务器流量预测值。

清单7显示了泛型PolynomialForecaster类的使用以及Forecaster的实现,以定义CubicForeaster类。 Apache Commons Math库提供了基础的建模算法。

清单7.三次预报器的实现是用Java实现的。
public class CubicForecaster extends PolynomialForecaster implements Forecaster {
	public static final Duration FORECAST_HORIZON = Duration.standardMinutes(120);

	public CubicForecaster(final Duration forecastHorizon) {
		super(forecastHorizon);
	}

	public CubicForecaster() {
		this(FORECAST_HORIZON);
	}

	@Override
	protected double[] apacheFitter(final double[] xs, final double[] ys, final double maxY) {
		final double defaultRelativeThresh = GUASSIAN_RELATIVE_THRESH * Precision.EPSILON;
		final double defaultAbsThresh = GUASSIAN_MIN_THRESH * Precision.SAFE_MIN;
		final ConvergenceChecker<PointVectorValuePair> valueChecker = new SimpleVectorValueChecker(defaultRelativeThresh, defaultAbsThresh);
		final PolynomialFitter pf = new PolynomialFitter(new GaussNewtonOptimizer(true, valueChecker));
		for (int i = 0; i < xs.length; i++) {
			pf.addObservedPoint(ys[i] / maxY, xs[i], ys[i]);
		}
		return pf.fit(new double[] { 0, 0, 0, 0 });
	}
}

剩余预报员

残差预测器考虑了我们数学库中的残差插值误差。 预报员在24小时内将历史需求的幅度与未来的预测需求进行匹配。 所得残差曲线是由实际需求值和插值之间的百分比误差确定的最大曲线。 最终预测趋势乘以残差曲线以调整残差插值误差。 图8总结了该算法。

图8.残余预测器的一般逻辑解释了数学插值误差。

预报员队伍

如图5所示,历史或周期性队列包含多个时间序列预测。 每个预报员都有一个互补的时间范围进行预报。 到未来越远,每个预测者引入预测的不确定性就越大。 但是,每个预报员的准确性在不同的时间段是不同的。 例如,当时间范围更接近当前时间段时,矢量和多项式预测器的权重应比调整后的预测器和部分预测器的权重更大,但随着预测的延伸,未来的权重应更小。 将归一化半衰期权重应用于队列中每个预测者的每个预测,以减少不确定性。 下面的等式描述了标准的半衰期符号,其中

是最初的需求
是时间单位,并且
是半衰期的估算值。

下一个方程式计算预测重量

预报员
在时间
给定分钟数,
,已经过去了。 为了减轻重量衰减的程度,用“ 2”代替
(在上述等式中)并将初始权重值标准化为1。最后,每个预测者的半衰期值是预测中最大分钟数的一半。

如下面的方程式所示,将每个预报器的权重进行归一化,以使特定预报时间的所有预报器权重的总和等于1。因此,给定一个重量。

清单8显示了一些Java代码,这些代码用于计算每个预报器的权重,然后以给定的时间单位生成最终的预报值。

清单8. Java代码描述了预测权重标准化过程的实现。
double weight;
				// check to see if this is a forecast, otherwise, this is a historical trend
				// the overall equation is the half: N(t) = No * e ^ (-t/T); No = original value, t = amount of time that has passed, T=the average half life
				// for our purposes, No=1 since we start with a weight of 1. Also, e decays too quickly so we use 2.
				// Our equation reduces, 2^((-m/h)*alpha); m=relative minutes that has passed, h=half life or mid point of our time horizon, alpha helps to ensure that we do not
				// have weights of zero.
				if (relativeMinutes > 0) {
					// if the forecast horizon is a "filler" or guarantees 24 hours, we want to find what is actually being forecast from the standard day minutes and then divide
					// by 2 to find the half point.
					if (forecaster.getForecastHorizon().getStandardMinutes() == Duration.standardDays(1).getStandardMinutes()) {
						weight = Math.pow(2, -relativeMinutes / (((Duration.standardDays(1).getStandardMinutes() - Minutes.minutesBetween(start, now).getMinutes()) / 2) * 1.1));
					} else {
						// the mid point of the forecast horizon is used as the mid point
						weight = Math.pow(2, -relativeMinutes / (forecaster.getForecastHorizon().getStandardMinutes() / 2) * 1.1);
					}
				} else {
					weight = 1;
				}
				// all weights for a particular minute from all forecasters is summed to adjust the sum of weights to 1
				sumWeights += weight;
				// store each weight
				weights.add(weight);
				// store each forecast value
				forecastScores.add(nearestInstant.getValue());

int parabolaMidX = Minutes.minutesBetween(new DateTime(start.withZone(DateTimeZone.forOffsetHours(config.getSite().getTimezoneOffset()))).withTimeAtStartOfDay(),
						new DateTime(maxTime.withZone(DateTimeZone.forOffsetHours(config.getSite().getTimezoneOffset())))).getMinutes();
				if (parabolaMidX < 0) {
					parabolaMidX += Days.ONE.toStandardMinutes().getMinutes();
				} else if (parabolaMidX > Days.ONE.toStandardMinutes().getMinutes()) {
					parabolaMidX -= Days.ONE.toStandardMinutes().getMinutes();
				}
				final double parabolicScalar = (watson.getParabola().getSlope() * Math.pow(minuteOffset - parabolaMidX, 2)) + watson.getParabola().getPeak();
				final double modWatsonBooster = watson.getBooster() * parabolicScalar;
if (modWatsonBooster * interpolation > ProvisionInstants.REQUESTS_PER_SERVER) {
					adjustedForecast = modWatsonBooster * interpolation;
					// use the maximum of cyclic or event -- maximum method
					final double currentInterpolation = interpolation(currentInterpolator, current, instant);
					double yValue = Math.max(adjustedForecast, currentInterpolation);
					combinedForecasts.add(instant.setValue(yValue));

					long eventPredictedTime = config.getDefaultEventDuration();
					EventPredictionCount eventPrediction = new EventPredictionCountDAO(site.get(), normalizeTime(instant.getInstant()).getMillis(), eventPredictedTime,
							(long) adjustedForecast, adjustedForecast > currentInterpolation);
					eventPredictions.add(eventPrediction);
				} else {
					skip++;
					combinedForecasts.add(instant);
				}

数据后处理

时间序列后处理器可提供平滑效果并减少错误流量,Twitter,分数,模拟,播放器,Web语义数据和计划数据的影响,同时遵循积极的配置策略。 预测后处理器是在历史预测器和峰值预测器之后实施的; 但是,在本文中,我们的讨论仅限于历史预测者之后的时间。

通过半衰期加权算法进行的最终组合历史预测会产生嘈杂且僵化的预测。 为了准备与峰值预测结合的循环模式,一些后处理器可以提高峰值预测器的准确性和互补性。 在我们的职业高尔夫和网球赛事中,由于消息传递延迟,可能会缺少地面真实数据或实时数据。 结果,在预处理中类似的干扰物会填充丢失的数据。 在某些情况下,预报员会产生平坦曲线或重复幅度,而服务器流量通常不会发生这种情况。 平线后处理器通过平均相应的历史数据来删除长线段。 此外,由于PCC在世界各地的事件期间运行,因此时移后处理器会更改预测曲线的时区。

云配置

服务器根据预测器集合的预测需求输出自动配置。 对于预测的每一分钟,都会计算要配置的服务器数量。 如果预测值太低以至于没有其他任何方式进行调配,则PCC始终会调配至少一台服务器。 当用Java实现时,下面的等式确定了要配置或取消配置的云资源数量。

一次要配置的服务器数

由表示
。 服务器的总容量取决于硬件,表示为
。 通过实验并考虑到我们的风险承受能力,我们在恒定的缓冲区内减轻了预测误差,
。 缓冲区的价值是根据历史事件趋势和逐年增长确定的。

在每个高尔夫和网球比赛中,云服务的供应或取消供应都是实时发生的。

结果

在整个比赛日程中,在典型的比赛日内,我们在三活动云中节省了多达51%的计算时间或134.5个小时的计算时间。 以这种速度,在107个比赛日中,PCC最多可以节省14388小时的计算资源。 此外,表1显示了PCC的精度。 仅基于周期的预测器产生的平均绝对百分比误差(MAPE)为18.44%。 清单9显示了用于计算MAPE的代码。

在本系列的下一篇文章中,您将发现通过添加峰值预测器,我们进一步将错误减少了8%以上。 预测的高精度和减少的计算时间浪费肯定超过了云服务配置不足的风险。

表1.准确性度量MAPE使我们能够度量预测的绩效。
预测类型 平均绝对百分比误差(MAPE) 平均绝对误差(HPM) 平均误差(HPM) 均方根误差(RMSE)
基于循环 18.44% 7,968 7,209 14,608
周期性与事件 10.25% 3,256 1,528 4,993
清单9.用Java实现的MAPE度量。
public class MeanAbsolutePercentageError implements ErrorMeasure {

	private final ErrorConditionMeasure condition;

	public MeanAbsolutePercentageError(ErrorConditionMeasure condition) {
		super();
		this.condition = condition;
	}

	@Override
	public double calcError(ImmutableList<ForecastInstant> groundTruth, ImmutableList<ForecastInstant> forecast) throws ErrorMeasureException {
		condition.canCalc(groundTruth, forecast);
		double delta = 0;
		for (int errorLoop = 0; errorLoop < Iterables.size(groundTruth); errorLoop++) {
			delta += Math
					.abs((Iterables.get(groundTruth, errorLoop).getValue() - Iterables.get(forecast, errorLoop).getValue()) / Iterables.get(groundTruth, errorLoop).getValue());

		}
		return (delta / Iterables.size(groundTruth)) * 100;

	}
}

为了通过PCC预测可视化数据,网站管理员,客户,开发人员,经理和顾问可以使用几个仪表板。 图9描绘了“预测的云计算”预测的实时可视化。 右侧的黄线是时间序列成分,而左侧的红线是基于事件的成分。 蓝线是服务器容量。 目标是永远不要让流量超出云的服务能力。 否则,最终用户将收到资源错误,并且无法访问体育网站的某些部分。 随着时间的流逝,垂直线将向前发展。 垂直线右边的任何图形都是预测。

图9.总体预测显示在实时仪表板上。

图10中显示的整个比赛仪表板提供了比赛摘要。 几条火花线显示有关特定锦标赛的鸣叫速度,以及我们所有原始机器上的日志访问。 分布式链模拟器可产生预测的比赛时间表,并在火花线下方显示。 汇总的预测趋势位于模拟锦标赛的下方。 其他视图显示播放器受欢迎程度,Web流量趋势,内容爬网结果,预测器对预测的贡献以及一般系统消息趋势。

图10.总体仪表板使用户可以查看不同类型的数据和PCC的结果。

结论

在本文中,我们展示了PCC如何使用自定义的预测框架来确定要为将来的工作负载供应或取消供应多少云资源。 随着体育赛事的实时进行,IBM InfoSphere Streams和RabbitMQ使PCC能够在一分钟的间隔内处理和累积日志。 通过IBM InfoSphere BigInsights将大量日志存储在Hadoop分布式文件系统(HDFS)中,并在指定的时间段内进行汇总。 对预测框架的输入进行了预处理,以确保每个时间块都是统一且一致的,而对输出进行后处理以进一步提高系统的准确性。 定制代码和Apache Math Common库支持我们算法中的数学运算。 几个公式,代码块和评估指标表描述了具体的实现细节。

在本系列的第9部分中,我们将讨论在整个PCC系统中使用的分布式特征提取和预测模型,该模型是对正弦预测系统的补充。


翻译自: https://www.ibm.com/developerworks/analytics/library/ba-predictive-cloud-computing-forecasting-trs/index.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值